精华内容
下载资源
问答
  • 壁板颤振POD算法

    2015-05-22 19:56:13
    壁板颤振的POD算法,二维壁板颤振的POD算法
  • 由一系列已知的相近流场解重新组合成一组正交分解(POD)的基模态。利用这些模态的线性组合构造绕一任意翼型的流场,使计算的未知量数目仅为所取相近解流场个数,而传统方法的未知量数目为网格单元数的4倍,因此极大...
  • 算法,不只是效率高就是好算法,用户用得开心才是好算法,下面这篇文章就是个很好的例子。——Silence   [转]从随机播放算法看 iPod 的细节之美 <br />似 乎从数码播放器出现的那一天开始就...

    算法,不只是效率高就是好算法,用户用得开心才是好算法,下面这篇文章就是个很好的例子。——Silence

     

    [转]从随机播放算法看 iPod 的细节之美

    似 乎从数码播放器出现的那一天开始就有了一个功能叫做随机播放,许多朋友都不喜欢按照顺序听歌,而喜欢那种随机的感觉。正如苹果的 iPod Shuffle ,字面意思就是洗牌,而宣称的广告词更是凸显了对这个功能的重视:“Life is random” 。这种你永远不知道下一首歌是什么的感觉,牢牢的抓住了那些喜欢新奇,喜欢惊喜的消费者的心理。但是,随机播放这个功能现在是个播放器都具有,为何单单是 苹果的 iPod 却让不少人感觉特别的舒服呢?

    不知道大家发现没有,其实随机播放的常见算法分为两种,一种是 iPod 这种的,类似洗牌的方法,我暂且叫它“洗牌”算法。也就是说,当你在播放器中载入一些歌曲后,系统会把这些歌曲放在一个列表里,然后对这个列表进行一种反 排序,也就是把次序打乱,和我们平时打牌的时候洗牌一样,这样我们就获得了一个乱序的播放列表,依次播放这个列表就可以实现乱序的播放了。另一种是大部分 的播放器具有的,是进行一个随机数的运算。也就是通过随机函数从播放列表中随机抽取一个歌曲进行播放,和抓阄的感觉差不多的方法,这里我叫它为“抓阄”算 法。

    这里我们不难看出,大部分播放器用的“抓阄”式的算法有个一些比较明显的缺陷:

    1、 有一定的概率连续选取到一样的曲子。其实这个只要你歌曲比较多的时候还是很少见的,但是由于一般的播放器的随机数的随机性都不是那么的好,往往某几首歌曲 会很少很少选中,尤其是靠近首尾的歌曲。 虽然没法看到播放器中的随机数生成的算法,但是我们不难推测,大部分的播放都采用了现在最流行,也是比较快捷的一种算法──线性同余算法,或者就是这种算 法的略微修改版本。这种算法在很多地方都普遍应用,比如 C 语言的标准库中就是利用了它产生随机数的。但是这个算法虽然是均匀分布的随机数,但是它是有缺陷的。简单的说,这个算法依赖于你给的随机数的上限,如果上 限越小,那重复的概率就会越大。所以在播放器中,受限于整数最大值的关系,重复的概率还是蛮大的。

    2、第二种“抓阄”式随机算法还有个很严 重的问题,就是当你听了一首歌曲,感觉很爽想再听的时候,你按下上一首,发现随机的跳跃到另外的歌曲去了,相信这个会让很多人很恼火。也就是当这种算法启 动之后,你的播放器的“下一首”和“上一首”成为了完全一样的存在。 当然,这还是有办法解决的,比如说设计一个记录表,标记已经播放了的曲子,然后下次就不要选中了,同时记录也可以实现退回上一首歌曲的功能。但是这样就必 须让播放器提供额外的空间来储存这些内容,这不得不说是一个不“美”的解决方案。

    介于上面两个问题,iPod 使用的“洗牌”式的算法就解决的相对更完美些。我们无法看到算法的本体,所以也无法从传统意义上的时空效率来评价 iPod 的算法,但是单单就是解决了上面两个问题,就足以让用户体验提升不止一个档次。

    真正的随机,其实并不讨人喜欢。人们喜欢的,是那种精心安排的偶遇。但是,苹果这种算法,做到了“可以追溯,可以回忆”,就像年华似水,可以溯游而上,倾心追忆,已经很好。毕竟,生活本身就不是把类似旋律的安排在一起,life is random.

    写到这里突然想起一句话,越优秀的算法总是越贴近生活。而这种贴近生活的感觉,也许正是组成 iPod 所向披靡的魅力的成分吧。

    出处:http://www.ifanr.com/29498

    展开全文
  • Pod

    2021-03-08 13:54:33
    文章目录pod介绍查看容器的网络initcontainers初始化容器Container容器镜像拉取策略(image PullPolicy)k8s私有仓库的创建上传镜像到私有仓库 pod介绍 pod是k8s的最小部署单元 是一组容器的集合,即一个pod中可能...

    pod介绍

    • pod是k8s的最小部署单元
    • 是一组容器的集合,即一个pod中可能包含多个容器
    • 一个pod中的容器共享网络命令空间

    查看容器的网络

    其实每次创建一个pod的时候,都会对应的去创建一个网络。

    [root@server3 ~]# cat /opt/kubernetes/cfg/kubelet
    
    KUBELET_OPTS="--logtostderr=true \
    --v=4 \
    --hostname-override=192.168.10.13 \
    --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
    --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
    --config=/opt/kubernetes/cfg/kubelet.config \
    --cert-dir=/opt/kubernetes/ssl \
    --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"
    

    其中–pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0"表示pod创建时创建的网络。

    • 然后通过docker ps也可以查看到前面所创建的pod网络。
      在这里插入图片描述

    initcontainers初始化容器

    于业务容器开始执行,原先Pod中容器是并行开启,现在进行了改进

    Container容器

    镜像拉取策略(image PullPolicy)

    IfNotPresent:默认值,镜像在宿主机上不存在时才拉取
    Always:每次创建Pod都会重新拉取一次镜像
    Never:Pod永远不会主动拉取这个镜像

    在这里插入图片描述使用以下的命令可以查看并修改到当前使用的控制器拉取镜像的策略

    kubectl edit deployment/nginx	#在master中操作
    
     ……
       template:
        metadata:
          creationTimestamp: null
          labels:
            run: nginx
        spec:
          containers:
          - image: nginx:latest
            imagePullPolicy: Always
            name: nginx
            ports:
            - containerPort: 80
              protocol: TCP
    ……
    
    • 通过以上的指令可以编写yaml文件来是实现pod的创建。
      首先编写yaml文件:
    [root@localhost demo]# vim pod1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
        - name: nginx
          image: nginx
          imagePullPolicy: Always
          command: [ "echo", "SUCCESS" ]
    

    在这里插入图片描述结果:虽然说我们能够成功去创建pod资源,但是这个pod资源一直处于ContainerCreating的状态,并非正常的Running状态。
    **原因:**失败的原因是因为命令冲突,其中==command: [ “echo”, “SUCCESS” ]==命令与我们的阿里云镜像相冲突,所以会失败。
    **解决:**删除掉冲突的命令行,并且制定需要安装的镜像为nginx:1.14

    kubectl delete -f pod1.yaml	#删除之前通过pod1.yaml文件创建的pod资源
    
    vim pod1.yaml	# 修改yaml文件
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
        - name: nginx
          image: nginx:1.14
          imagePullPolicy: Always
    
    kubectl apply -f pod1.yaml 	# 更新资源
    
    kubectl get pods	# 查看pod资源
    NAME                    READY   STATUS    RESTARTS   AGE
    mypod                   1/1     Running   0          3s
    

    然后我们可以通过 -o wide来查看到此时pod运行的容器的IP地址。

    kubectl get pods -o wide
    NAME                    READY   STATUS    RESTARTS   AGE    IP            NODE            NOMINATED NODE   READINESS GATES
    mypod                   1/1     Running   0          12s    172.17.79.7   192.168.10.14   <none>           <none>
    

    因为现在pod中的容器的服务并没有发布,所以想直接通过web访问时不行的,但是我们可以通过node节点来访问。

    [root@server3 ~]# curl -I 172.17.79.7
    HTTP/1.1 200 OK
    Server: nginx/1.14.2
    Date: Mon, 08 Mar 2021 07:05:31 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 04 Dec 2018 14:44:49 GMT
    Connection: keep-alive
    ETag: "5c0692e1-264"
    Accept-Ranges: bytes
    

    k8s私有仓库的创建

    首先在初始化的状态下下载安装docker服务,前面有说到。
    在安装完成的docker的主机中安装harbor。其中需要的组件由docker-compose与harbor-offline-installer-v1.2.2.tgz。其中docker-compose为一个二进制的可执行的包,而harbor-offline-installer-v1.2.2.tgz为一个压缩包。

    cp docker-compose /usr/local/bin
    chmod +x /usr/local/bin/docker-compose
    
    tar zxvf harbor-offline-installer-v1.2.2.tgz -C /usr/local
    cd /usr/local/harbor/
    
    [root@localhost harbor]# ls
    common                     harbor_1_1_0_template  LICENSE
    docker-compose.clair.yml   harbor.cfg             NOTICE
    docker-compose.notary.yml  harbor.v1.2.2.tar.gz   prepare
    docker-compose.yml         install.sh             upgrade
    
    • 首先先对harbor的配置文件进行修改
    vim harbor.cfg
    hostname = 192.168.10.20	# 需要将IP修改为自己的harbor仓库的IP地址
    
    harbor_admin_password = Harbor12345	# 不需要改动,但是后面登录harbor的时候需要使用到
    
    • 运行install.sh脚本文件
    [root@localhost harbor]# ./install.sh 
    
    [Step 0]: checking installation environment ...
    
    Note: docker version: 20.10.5
    
    Note: docker-compose version: 1.27.4
    
    [Step 1]: loading Harbor images ...
    Loaded image: vmware/harbor-ui:v1.2.2
    Loaded image: vmware/notary-photon:server-0.5.0
    Loaded image: vmware/nginx-photon:1.11.13
    Loaded image: vmware/registry:2.6.2-photon
    Loaded image: photon:1.0
    Loaded image: vmware/notary-photon:signer-0.5.0
    Loaded image: vmware/harbor-adminserver:v1.2.2
    Loaded image: vmware/harbor-log:v1.2.2
    Loaded image: vmware/harbor-db:v1.2.2
    Loaded image: vmware/harbor-jobservice:v1.2.2
    Loaded image: vmware/harbor-notary-db:mariadb-10.1.10
    Loaded image: vmware/clair:v2.0.1-photon
    Loaded image: vmware/postgresql:9.6.4-photon
    
    
    [Step 2]: preparing environment ...
    Clearing the configuration file: ./common/config/adminserver/env
    Clearing the configuration file: ./common/config/ui/env
    Clearing the configuration file: ./common/config/ui/app.conf
    Clearing the configuration file: ./common/config/ui/private_key.pem
    Clearing the configuration file: ./common/config/db/env
    Clearing the configuration file: ./common/config/jobservice/env
    Clearing the configuration file: ./common/config/jobservice/app.conf
    Clearing the configuration file: ./common/config/registry/config.yml
    Clearing the configuration file: ./common/config/registry/root.crt
    Clearing the configuration file: ./common/config/nginx/nginx.conf
    loaded secret from file: /data/secretkey
    Generated configuration file: ./common/config/nginx/nginx.conf
    Generated configuration file: ./common/config/adminserver/env
    Generated configuration file: ./common/config/ui/env
    Generated configuration file: ./common/config/registry/config.yml
    Generated configuration file: ./common/config/db/env
    Generated configuration file: ./common/config/jobservice/env
    Generated configuration file: ./common/config/jobservice/app.conf
    Generated configuration file: ./common/config/ui/app.conf
    Generated certificate, key file: ./common/config/ui/private_key.pem, cert file: ./common/config/registry/root.crt
    The configuration files are ready, please use docker-compose to start the service.
    
    
    [Step 3]: checking existing instance of Harbor ...
    
    
    [Step 4]: starting Harbor ...
    Creating network "harbor_harbor" with the default driver
    Creating harbor-log ... done
    Creating harbor-adminserver ... done
    Creating harbor-db          ... done
    Creating registry           ... done
    Creating harbor-ui          ... done
    Creating harbor-jobservice  ... done
    Creating nginx              ... done
    
    ✔ ----Harbor has been installed and started successfully.----
    
    Now you should be able to visit the admin portal at http://192.168.10.20. 
    For more details, please visit https://github.com/vmware/harbor .
    

    这样的话harbor仓库就建立好了,此时只要通过harborIP登录到web网页。
    在这里插入图片描述

    上传镜像到私有仓库

    node节点保证能够从私有仓库中下载镜像,首先得在配置文件中添加私有仓库的地址(每个节点都要做)。

    vim /etc/docker/daemon.json
    {
      "registry-mirrors": ["https://3m0ss.mirror.aliyuncs.com"],
      "insecure-registries":["192.168.10.20"]
    }
    

    ==注意:==第一行后面一定要有**(,)**,下面的地址是harbor仓库的IP地址

    1. 首先在我们的node节点通过docker login登录到harbor仓库
      在这里插入图片描述
    2. 通过docker pull从公共仓库下载镜像到本地;

    在这里插入图片描述

    1. 将刚下下载下来的镜像打上标签
      在这里插入图片描述
    2. 将刚才打有标签的镜像上传到私有仓库
      在这里插入图片描述
    3. 然后通过web网页登录到私有仓库,可以看到刚才上传的镜像。
      在这里插入图片描述

    私有仓库的问题

    上面我们成功进行docker私有仓库的建立,但是当在其他的节点去私有仓库下载的时候发现无法下载,所以需要去解决这问题。

    [root@localhost ~]# docker pull 192.168.10.20/project/tomcat
    Using default tag: latest
    Error response from daemon: pull access denied for 192.168.10.20/project/tomcat, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
    

    并且即使有node节点上面已经拉取了镜像资源,但是在使用该镜像资源创建pod的后,仍然会显示镜像拉取失败的提示信息。
    在这里插入图片描述

    pod资源控制

    官方网站:

    https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
    
    Pod和Container的资源请求和限制:
    spec.containers[].resources.limits.cpu     //cpu上限 
    spec.containers[].resources.limits.memory   //内存上限
    spec.containers[].resources.requests.cpu   //创建时分配的基本CPU资源
    spec.containers[].resources.requests.memory  //创建时分配的基本内存资源
    

    示例:

    [root@server1 demo]# cat pod1.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: frontend
    spec:
      containers:
      - name: db
        image: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "password"
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
      - name: wp
        image: wordpress
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
    

    **解析:**其中有db和wp两个数据库,每个创建时的基本CPU都是25%,基本内存资源都是64M。每个最大的CPU都是50%,最大内存都是128M。

    • 通过yaml文件创建pod资源。
    ## 方式一
    kubectl apply -f pod1.yaml
    
    ## 方式二
    kubectl create -f pod1.yaml
    
    • 查看具体事件
     kubectl describe pod frontend
    

    在这里插入图片描述

    kubectl describe nodes 192.168.10.14
    

    在这里插入图片描述可以看见之前设置的资源控制已经生效。
    所以由资源控制生成的pod资源完成。

    查看名称空间

    [root@server1 demo]# kubectl get ns
    NAME              STATUS   AGE
    default           Active   3d2h
    kube-node-lease   Active   3d2h
    kube-public       Active   3d2h
    kube-system       Active   3d2h
    

    重启策略

    当Pod在遇见故障之后重启的动作(k8s中不支持重启Pod资源,只有删除重建)

    • Always:当容器终止退出后,总是重启容器,默认策略
    • OnFailure:当容器异常退出(退出状态码非0)时,重启容器
    • Never:当容器终止退出,从不重启容器。

    查看当前控制器的重启策略

    kubectl edit deploy·		#首先得确保目前有控制器在运行,即使用get deployment能够查询到控制器
    

    在这里插入图片描述

    模拟Always重启策略

    [root@server1 demo]# cat pod2.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: foo
    spec:
      containers:
      - name: busybox
        image: busybox
        args:
        - /bin/sh
        - -c
        - sleep 30; exit 3
    
    kubectl apply -f pod2.yaml	#根据yaml文件创建pod
    

    在这里插入图片描述可以查看到pod一直在重启这样的容器,因为本身默认的重启机制是Always,所以一旦出现终止退出后总是在重启。

    模拟Never重启策略

    根据之前的定义,就是说容器终止后,pod不会去重启容器。

    [root@server1 demo]# cat pod2.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: foo
    spec:
      containers:
      - name: busybox
        image: busybox
        args:
        - /bin/sh
        - -c
        - sleep 10
      restartPolicy: Never
    
    [root@server1 demo]# kubectl delete -f pod2.yaml 
    pod "foo" deleted
    

    在这里插入图片描述
    可以发现与前面不同的是,容器在退出后,pod不会再去重启容器资源,因为正常退出后,重启策略为Never,所以不会再去重启。

    探针(Probe)

    pod的健康检查

    Probe探针的规则

    livenessProbe

    • 用于判断Container是否处于运行状态
    • 当服务crash或者死锁等情况发生时,kubelet会kill掉Container,然后根据其设置的restart policy进行相应操作

    ReadinessProbe

    • 用于判断服务是否已经正常工作
    • 如果服务没有加载完成或工作异常,服务所在的Pod的IP地址会从服务的Endpoints中被移除,也就是说,当服务没有ready时,会将其从服务的load balancer中移除,不会再接受或响应任何请求。

    两种规则并无冲突,可以同时进行使用。

    Probe探针检查方法

    • httpGet 发送http请求,返回200-400范围状态码为成功。
    • exec 执行Shell命令返回状态码是0为成功。
    • tcpSocket 发起TCP Socket建立成功

    示例

    exec方式

    使用livenessProbe规则搭配exec检查方法进行pod的健康检查。

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

    通过get pods指令可以持续查看到liveness-exec资源会处在一直的运行和关闭状态,因为默认规则为always,所以当创建的文件被删除之后,会触发livenessProbe探针规则,进行不停的重启操作。

    调度约束

    Kubernetes通过watch的机制进行每个组件的协作,每个组件之间的设计实现了解耦。

    调度约束的步骤(理解)

    在这里插入图片描述

    组件

    • API Server:作为集群内部和运维人员的唯一交互入口(即所有的指令首先得到达该组件)
    • etcd:所有数据的存储以及操作记录都在etcd中进行存储
    • Scheduler:为Pod提供调度服务
    • Kubelet:处理Master节点下发到本节点的任务,管理Pod和其中的容器
    • Docker:创建容器

    过程

    1. 运维人员向k8s平台发送创建pod的请求,API Server将创建该资源的属性信息写入到etcd群集中;
    2. API Server通过watch机制向Scheduler调度器发送请求分配后台node的相关信息,Scheduler调度器通过评估后台node节点的性能,选出适合建立pod的node节点(假设为node01)进行绑定,然后将这样绑定的网络信息反馈到API Server,API Server将网络信息写到etcd中;
    3. API Server会通过watch机制向node01节点的Kubelet进行通知,kubelet会触发容器命令,进行容器的创建。创建后,docker会反馈一条状态信息,最终反馈到API Server,API Server会将最终的状态信息写入到etcd中。

    调度方式

    • 直接调度:nodeName用于将Pod调度到指定的Node名称上(跳过调度器直接分配)
    • **选择器:**nodeSelector用于将Pod调度到匹配Label的Node上

    直接调度(nodename)

    [root@server1 demo]# cat pod5.yaml 
    apiVersion: v1  
    kind: Pod  
    metadata:
      name: pod-example  
      labels:
        app: nginx  
    spec:
      nodeName: 192.168.10.14
      containers:
      - name: nginx  
        image: nginx:1.15
    

    在这里插入图片描述通过 kubectl describe pod [pod name] 来查看pod创建的状态,可以看到未经过Scheduler调度器。

    选择器(nodeSelector)

    在设置选择器的调度算法之前,首先得对我们后台的node节点进行打标签。

    ① 打标签

    [root@server1 demo]# kubectl label nodes 192.168.10.13 ID=hz
    node/192.168.10.13 labeled
    [root@server1 demo]# kubectl label nodes 192.168.10.14 ID=sh
    node/192.168.10.14 labeled
    

    通过以下命令可以查看到各节点被打上的标签信息:

    kubectl get nodes --show-labels
    

    在这里插入图片描述

    ② 编写选择器调度yaml文件

    [root@server1 demo]# cat pod6.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-example
      labels:
        app: nginx
    spec:
      nodeSelector: 
        ID: sh
      containers:
      - name: nginx
        image: nginx:1.15
    

    在这里插入图片描述其中也可以明显的查看到不进经过了调度器,并且调度器将资源直接分配到后台的192.168.10.14节点,也就是我们在编写yaml脚本的时候中指定的ID=sh的标签的节点。
    说明nodeSelector调度成功。

    配置管理

    配置管理可以再创建pod时将加密数据存放在pod运行的etcd中。
    其次还可以让pod容器以挂载volume的方式访问到挂载在pod指定的路径下。

    Secret

    用于加密存放在etcd中,创建pod后可以访问

    创建凭据

    方式一:

    将数据传到文件,通过文件来创建凭据。

    [root@server1 demo]# echo -n 'admin' > ./username.txt
    [root@server1 demo]# echo -n 'asdjdjsioc' > ./password.txt
    
    [root@server1 demo]# ls
    password.txt  username.txt
    
    [root@server1 demo]# kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
    secret/db-user-pass created
    
    [root@server1 demo]# kubectl get secret
    NAME                  TYPE                                  DATA   AGE
    db-user-pass          Opaque                                2      7s
    
    [root@server1 demo]# kubectl describe secret db-user-pass
    Name:         db-user-pass
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    
    Type:  Opaque
    
    Data
    ====
    password.txt:  10 bytes
    username.txt:  5 bytes
    

    方式二:

    通过yaml文件进行数据的传入。

    • 首先将想要传入的参数修改为yaml格式能够识别的64转码。
    [root@server1 demo]# echo -n 'admin' | base64
    YWRtaW4=
    
    [root@server1 demo]# echo -n 'asdjdjsioc' | base64
    YXNkamRqc2lvYw==
    
    vim secret.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    type: Opaque
    data:
      username: YWRtaW4=
      password: YXNkamRqc2lvYw==
    
    [root@server1 demo]# kubectl apply -f secret.yaml 
    secret/mysecret created
    
    [root@server1 demo]# kubectl get secret
    NAME                  TYPE                                  DATA   AGE
    db-user-pass          Opaque                                2      11m
    default-token-lx6bd   kubernetes.io/service-account-token   3      3d14h
    mysecret              Opaque 
    
    [root@server1 demo]# kubectl describe secret mysecret
    Name:         mysecret
    Namespace:    default
    Labels:       <none>
    Annotations:  
    Type:         Opaque
    
    Data
    ====
    password:  10 bytes
    username:  5 bytes
    

    变量导入

    我们在前面将想要传导的数据已经传导secret中,现在怎么讲secret中的变量导入到pod中去。

    方式一:

    使用secret中的变量导入到pod中。
    在编写yaml脚本文件的时候,指定使用secret中的指定pod中的参数。

    vim secret-var.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: nginx
        image: nginx
        env:
          - name: SECRET_USERNAME
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: username
          - name: SECRET_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: password
    
    kubectl apply -f secret-var.yaml
    pod/mypod created
    
    [root@server1 demo]# kubectl get pods
    NAME    READY   STATUS    RESTARTS   AGE
    mypod   1/1     Running   0          9s
    
    [root@server1 demo]# kubectl exec -it mypod bash	# 进入到pod中,查看传过来的参数
    root@mypod:/# ls
    bin   docker-entrypoint.d   home   media  proc	sbin  tmp
    boot  docker-entrypoint.sh  lib    mnt	  root	srv   usr
    dev   etc		    lib64  opt	  run	sys   var
    root@mypod:/# echo $SECRET_USERNAME
    admin
    root@mypod:/# echo $SECRET_PASSWORD
    asdjdjsioc
    

    方式二:

    以volume的形式挂载到pod的某个目录下
    通过挂载的形式将前面创建的secret挂载到pod容器中的指定目录下。

    vim secret-var.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
          readOnly: true
      volumes:
      - name: foo
        secret:
          secretName: mysecret
    
    [root@server1 demo]# kubectl apply -f secret-var.yaml 
    pod/mypod created
    
    [root@server1 demo]# kubectl get pods
    NAME    READY   STATUS    RESTARTS   AGE
    mypod   1/1     Running   0          16s
    
    [root@server1 demo]# kubectl exec -it mypod bash
    root@mypod:/# cd /etc/foo
    root@mypod:/etc/foo# ls
    password  username
    root@mypod:/etc/foo# cat password 
    asdjdjsiocroot@mypod:/etc/foo# cat username
    

    ConfigMap

    主要用于不需要加密配置信息的存储和导入。

    创建凭据

    方式一:kubectl

    [root@server1 demo]# cat redis.properties 	# 编写所要传入的参数
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456
    
    [root@server1 demo]# kubectl create configmap redis-config --from-file=redis.properties		# 通过redis.properties文件进行configmap资源的创建
    configmap/redis-config created
    
    [root@server1 demo]# kubectl get configmap	# 查看configmap资源
    NAME           DATA   AGE
    redis-config   1      11s
    
    [root@server1 demo]# kubectl describe configmap redis-config	# 描述configmap资源的具体信息
    Name:         redis-config
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    
    Data
    ====
    redis.properties:
    ----
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456
    
    Events:  <none>
    
    [root@server1 demo]# cat cm.yaml 	# 通过前面生成的configmap资源来创建新的pod,并将configmap中的参数传到pod中,显示出来
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
        - name: busybox
          image: busybox
          command: [ "/bin/sh","-c","cat /etc/config/redis.properties" ]
          volumeMounts:
          - name: config-volume
            mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: redis-config
      restartPolicy: Never
    
    [root@server1 demo]# kubectl apply -f cm.yaml 	# 通过yaml文件来创建pod资源
    pod/mypod created
    
    [root@server1 demo]# kubectl get pods
    NAME    READY   STATUS      RESTARTS   AGE
    mypod   0/1     Completed   0          5s
    [root@server1 demo]# kubectl logs mypod	# 因为我们在新的pod中使用cat命令查看所传进去的参数
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456
    [root@server1 demo]# vim redis.properties 
    [root@server1 demo]# cat redis.properties 
    redis.host=127.0.0.1
    redis.port=6379
    redis.password=123456
    

    方式二:

    变量参数形式

    vim myconfig.yaml		# 编写yaml文件,将想要传的数据以参数形式传入
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: myconfig
      namespace: default
    data:
      special.level: info
      special.type: hello
    
    [root@server1 demo]# kubectl apply -f myconfig.yaml 	# 通过yaml文件,来生成configmap资源
    configmap/myconfig created
    
    vim myconfig-var.yaml	# 编写yaml文件,将configmap的资源传入到新的pod中
    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
        - name: busybox
          image: busybox
          command: [ "/bin/sh", "-c", "echo $(LEVEL) $(TYPE)" ]
          env:
            - name: LEVEL
              valueFrom:
                configMapKeyRef:
                  name: myconfig
                  key: special.level
            - name: TYPE
              valueFrom:
                configMapKeyRef:
                  name: myconfig
                  key: special.type
      restartPolicy: Never
    
    [root@server1 demo]# kubectl apply -f myconfig-var.yaml 	
    pod/mypod created
    
    [root@server1 demo]# kubectl logs mypod	# 查看新的pod资源的信息
    info hello
    
    展开全文
  • 本文将对Kubernetes如何发布与管理容器应用进行详细说明,主要包括Pod概述、基本用法、生命周期、Pod的控制和调度管理、Pod的升级和回滚,以及Pod的扩容机制等内容,并结合具体详细的示例,带你轻松玩转Pod,开启...

    本文将对Kubernetes如何发布与管理容器应用进行详细说明,主要包括Pod概述、基本用法、生命周期、Pod的控制和调度管理、Pod的升级和回滚,以及Pod的扩容机制等内容,并结合具体详细的示例,带你轻松玩转Pod,开启Kubernetes容器的编排之路。

    在这里插入图片描述

    1、Pod概述

    1.1 Pod是什么?

    Pod是Kubernetes中的原子对象,是基本构建单元。

    Pod表示集群上一组正在运行的容器。通常创建Pod是为了运行单个主容器。Pod 还可以运行可选的sidecar容器,以实现诸如日志记录之类的补充特性。(如:在Service Mesh中,和应用一起存在的istio-proxyistio-init容器)

    一个Pod中可以包含多个容器(其他容器作为功能补充),负责处理容器的数据卷、秘钥、配置。
    在这里插入图片描述

    1.2 为什么要引入Pod概念?

    原因1:Kubernetes可扩展

    Kubernetes不会直接和容器打交道,Kubernetes的使用者能接触到的资源只有Pod,而Pod里可以包含多个容器。当我们在Kubernetes里用kubectl执行各种命令操作各类资源时,是无法直接操作容器的,往往都是借助于Pod。

    Kubernetes并不是只支持Docker这一个容器运行时。 为了让Kubernetes不和某种特定的容器运行时进行技术绑死,而是能无需重新编译源代码就能够支持多种容器运行时技术的替换,和我们面向对象设计中引入接口作为抽象层一样,在Kubernetes和容器运行时之间我们引入了一个抽象层,即容器运行时接口(CRI:Container Runtime Interface)。
    在这里插入图片描述

    借助CRI这个抽象层,使得Kubernetes不依赖于底层某一种具体的容器运行时实现技术,而是直接操作Pod,Pod内部再管理多个业务上紧密相关的用户业务容器,这种架构更便于Kubernetes的扩展。

    原因2:易管理

    假设Kubernetes中没有Pod的概念,而是直接管理容器,那么有些容器天生需要紧密关联,如:在ELK中,日志采集Filebeat需要和应用紧密部署在一起。如果将紧密关联的一组容器作为一个单元,假设其中一个容器死亡了,此时这个单元的状态应该如何定义呢?应该理解成整体死亡,还是个别死亡?

    这个问题不易回答的原因,是因为包含了这一组业务容器的逻辑单元,没有一个统一的办法来代表整个容器组的状态,这就是Kubernetes引入Pod的概念,并且每个Pod里都有一个Kubernetes系统自带的pause容器的原因,通过引入pause这个与业务无关并且作用类似于Linux操作系统守护进程的Kubernetes系统标准容器,以pause容器的状态来代表整个容器组的状态。
    在这里插入图片描述
    对于这些天生需要紧密关联的容器,可以放在同一个Pod里,以Pod为最小单位进行调度、扩展、共享资源及管理生命周期。

    原因3:通讯、资源共享

    Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂接的Volume,这样既简化了密切关联的业务容器之间的通信问题,也很好地解决了它们之间的文件共享问题。

    相同的namespace可以用localhost通信,可以共享存储等。

    1.3 Pod能够带来什么好处

    搞清楚了Pod的由来,它到底能够为我们带来哪些好处呢?

    • Pod做为一个可以独立运行的服务单元,简化了应用部署的难度,以更高的抽象层次为应用部署管提供了极大的方便。
    • Pod做为最小的应用实例可以独立运行,因此可以方便的进行部署、水平扩展和收缩、方便进行调度管理与资源的分配。
    • Pod中的容器共享相同的数据和网络地址空间,Pod之间也进行了统一的资源管理与分配。

    2、Pod基本用法

    无论通过命令kubectl,还是Dashboard图形管理界面来操作,都离不开资源清单文件的定义。如果采用Dashboard图形管理界面操作,最终还是基于kubectl命令操作的,这里只介绍使用kubectl命令来操作Pod。

    关于kubectl命令更多说明,可以参考官方文档:https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-strong-getting-started-strong-

    Pod资源清单中有几个重要属性:apiVersionkindmetadataspec以及status。其中apiVersionkind是比较固定的,status是运行时的状态,所以最重要的就是metadataspec两个部分。

    (Kubernetes资源清单的定义,可参考上一篇文章:Kubernetes资源清单篇:如何创建资源?)

    先来定义一个简单的Pod资源文件,命名为frontend-pod.yml:

    示例中的Pod是在命名空间test中定义的,所以接下来的执行命令中都涉及指定命名空间的参数-n test。如果在默认命名空间default中定义,无需指定参数-n执行。

    apiVersion: v1
    kind: Pod
    metadata:
      name: frontend
      namespace: test	# 如果没有命名空间test,需提前创建。也可以使用默认命名空间default,即:namespace属性标签可以不定义
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: xcbeyond/vue-frontend:latest		# 发布在DockerHub中的镜像
        ports:
          - name: port
            containerPort: 80
            hostPort: 8080
    

    可以使用命令kubectl explain pod来查看各个属性标签的具体用法及含义。

    [xcbeyond@bogon ~]$ kubectl explain pod
    KIND:     Pod
    VERSION:  v1
    
    DESCRIPTION:
         Pod is a collection of containers that can run on a host. This resource is
         created by clients and scheduled onto hosts.
    
    FIELDS:
       apiVersion	<string>
         APIVersion defines the versioned schema of this representation of an
         object. Servers should convert recognized schemas to the latest internal
         value, and may reject unrecognized values. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
    
       kind	<string>
         Kind is a string value representing the REST resource this object
         represents. Servers may infer this from the endpoint the client submits
         requests to. Cannot be updated. In CamelCase. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
    
       metadata	<Object>
         Standard object's metadata. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
    
       spec	<Object>
         Specification of the desired behavior of the pod. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    
       status	<Object>
         Most recently observed status of the pod. This data may not be up to date.
         Populated by the system. Read-only. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    

    2.1 创建

    基于资源清单文件来创建Pod,kubectl create -f <filename>

    [xcbeyond@localhost ~]$ kubectl create -f frontend-pod.yml
    pod/frontend created
    

    2.2 查看状态

    创建完Pod后,想知道Pod的运行状态,可通过命令kubectl get pods -n <namespace>查看:

    (default命名空间,可不指定-n参数,非default则需指定具体namespace,否则查询不到。)

    [xcbeyond@localhost ~]$ kubectl get pods -n test
    NAME       READY   STATUS    RESTARTS   AGE
    frontend   1/1     Running   0          36s
    

    2.3 查看配置

    如果想了解一个正在运行的Pod配置,可通过命令kubectl get pod <pod-name> -n <namespace> -o <json|yaml>查看:

    (-o参数用于指定输出配置格式,json、yaml格式)

    此时查看结果处于运行态的结果,其中包含很多属性,我们只需关注关键属性即可。

    [xcbeyond@localhost ~]$ kubectl get pod frontend -n test -o yaml
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: "2020-11-19T08:33:20Z"
      labels:
        app: frontend
      managedFields:
      - apiVersion: v1
        fieldsType: FieldsV1
        fieldsV1:
          f:metadata:
            f:labels:
              .: {}
              f:app: {}
          f:spec:
            f:containers:
              k:{"name":"frontend"}:
                .: {}
                f:image: {}
                f:imagePullPolicy: {}
                f:name: {}
                f:ports:
                  .: {}
                  k:{"containerPort":80,"protocol":"TCP"}:
                    .: {}
                    f:containerPort: {}
                    f:hostPort: {}
                    f:name: {}
                    f:protocol: {}
                f:resources: {}
                f:terminationMessagePath: {}
                f:terminationMessagePolicy: {}
            f:dnsPolicy: {}
            f:enableServiceLinks: {}
            f:restartPolicy: {}
            f:schedulerName: {}
            f:securityContext: {}
            f:terminationGracePeriodSeconds: {}
        manager: kubectl-create
        operation: Update
        time: "2020-11-19T08:33:20Z"
      - apiVersion: v1
        fieldsType: FieldsV1
        fieldsV1:
          f:status:
            f:conditions:
              k:{"type":"ContainersReady"}:
                .: {}
                f:lastProbeTime: {}
                f:lastTransitionTime: {}
                f:status: {}
                f:type: {}
              k:{"type":"Initialized"}:
                .: {}
                f:lastProbeTime: {}
                f:lastTransitionTime: {}
                f:status: {}
                f:type: {}
              k:{"type":"Ready"}:
                .: {}
                f:lastProbeTime: {}
                f:lastTransitionTime: {}
                f:status: {}
                f:type: {}
            f:containerStatuses: {}
            f:hostIP: {}
            f:phase: {}
            f:podIP: {}
            f:podIPs:
              .: {}
              k:{"ip":"172.18.0.5"}:
                .: {}
                f:ip: {}
            f:startTime: {}
        manager: kubelet
        operation: Update
        time: "2020-11-23T08:10:40Z"
      name: frontend
      namespace: test
      resourceVersion: "28351"
      selfLink: /api/v1/namespaces/test/pods/frontend
      uid: be4ad65c-e426-4110-8337-7c1dd542f647
    spec:
      containers:
      - image: xcbeyond/vue-frontend:latest
        imagePullPolicy: Always
        name: frontend
        ports:
        - containerPort: 80
          hostPort: 8080
          name: port
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: default-token-bbmj5
          readOnly: true
      dnsPolicy: ClusterFirst
      enableServiceLinks: true
      nodeName: minikube
      preemptionPolicy: PreemptLowerPriority
      priority: 0
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      serviceAccount: default
      serviceAccountName: default
      terminationGracePeriodSeconds: 30
      tolerations:
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 300
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 300
      volumes:
      - name: default-token-bbmj5
        secret:
          defaultMode: 420
          secretName: default-token-bbmj5
    status:
      conditions:
      - lastProbeTime: null
        lastTransitionTime: "2020-11-19T08:33:20Z"
        status: "True"
        type: Initialized
      - lastProbeTime: null
        lastTransitionTime: "2020-11-23T08:10:40Z"
        status: "True"
        type: Ready
      - lastProbeTime: null
        lastTransitionTime: "2020-11-23T08:10:40Z"
        status: "True"
        type: ContainersReady
      - lastProbeTime: null
        lastTransitionTime: "2020-11-19T08:33:20Z"
        status: "True"
        type: PodScheduled
      containerStatuses:
      - containerID: docker://84d978ee70d806d38b9865021d9c68881cf096960c7eb45e87b3099da85b4f6d
        image: xcbeyond/vue-frontend:latest
        imageID: docker-pullable://xcbeyond/vue-frontend@sha256:aa31cdbca5ca17bf034ca949d5fc7d6e6598f507f8e4dad481e050b905484f28
        lastState: {}
        name: frontend
        ready: true
        restartCount: 0
        started: true
        state:
          running:
            startedAt: "2020-11-23T08:10:40Z"
      hostIP: 172.17.0.2
      phase: Running
      podIP: 172.18.0.5
      podIPs:
      - ip: 172.18.0.5
      qosClass: BestEffort
      startTime: "2020-11-19T08:33:20Z"
    

    2.4 查看日志

    如果想查看标准输出的日志,可以通过命令kubectl logs <pod-name> -n <namespace>查看:

    [xcbeyond@localhost ~]$ kubectl logs frontend -n test
    

    如果Pod中有多个容器,查看特定容器的日志需要指定容器名称kubectl logs <pod-name> -c <container-name>

    2.5 修改配置

    如果想修改已创建的Pod,如修改某个标签label,有以下几种方式:

    (1)通过标签管理命令kubectl label设置或更新资源对象的labels。

    该种方式只是针对标签的修改。

    通过命令kubectl get pods -n <namespace> --show-labels查看标签

    [xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels
    NAME       READY   STATUS    RESTARTS   AGE   LABELS
    frontend   1/1     Running   0          4d    app=frontend
    

    通过命令kubectl label pod <pod-name> -n <namespace> <key=value>新增标签

    [xcbeyond@localhost ~]$ kubectl label pod frontend -n test tir=frontend
    pod/frontend labeled
    [xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels
    NAME       READY   STATUS    RESTARTS   AGE    LABELS
    frontend   1/1     Running   0          4d1h   app=frontend,tir=frontend
    

    通过命令kubectl label pod <pod-name> -n <namespace> <key=new-value> --overwrite修改标签

    [xcbeyond@localhost ~]$ kubectl label pod frontend -n test tir=unkonwn --overwrite
    pod/frontend labeled
    [xcbeyond@localhost ~]$ kubectl get pods -n test --show-labels
    NAME       READY   STATUS    RESTARTS   AGE    LABELS
    frontend   1/1     Running   0          4d1h   app=frontend,tir=unkonwn
    

    (2)通过命令kubectl apply -f <filename>命令对配置进行更新。

    (3)通过命令kubectl edit -f <filename> -n <namespace>命令对配置进行在线更新。

    (4)通过命令kubectl replace -f <filename> -n <namespace> --force命令强制替换资源对象。

    实际上,先删除在替换。

    [xcbeyond@localhost ~]$ kubectl replace -f frontend-pod.yml --force
    pod "frontend" deleted
    pod/frontend replaced
    

    2.6 删除

    通过命令kubectl delete (-f <filename> | pod [<pod-name> | -l label]) -n <namespace>进行删除。

    [xcbeyond@localhost ~]$ kubectl delete pod frontend -n test
    pod "frontend" deleted
    

    3、Pod生命周期

    Pod对象自从其创建开始至其终止退出的时间范围称为其Pod生命周期。在这段时间中,Pod会处于多种不同的状态,并执行一些操作。其中,创建主容器(main container)为必需的操作,其他可选的操作还包括运行初始化容器(init container)、容器启动后钩子(post start hook)、容器的存活性探测(liveness probe)、就绪性探测(readiness probe)以及容器终止前钩子(pre stop hook)等,这些操作是否执行则取决于Pod的定义。如下图所示:
    在这里插入图片描述

    Podstatus字段是一个PodStatus的对象,PodStatus中有一个phase字段。

    无论是手动创建还是通过Deployment等控制器创建,Pod对象总是处于其生命周期中以下几个阶段(phase)之一:

    • 挂起(PendingAPI Server已经创建了该Pod,并已存入etcd中,但它尚未被调度完成,或者仍处于从仓库下载镜像的过程中。
    • 运行中(RunningPod内所有容器均已创建,被调度至某节点,并且所有容器都已经被kubelet创建完成,且至少有一个容器处于运行状态、正在启动状态或正在重启状态。
    • 成功(SucceededPod内所有容器都已经成功执行后退出终止,且不会再重启。
    • 失败(FailedPod内所有容器都已退出,并且至少有一个容器是因为失败而终止退出。即容器以非0状态退出或者被系统禁止。
    • 未知(Unknown:由于某种原因Api Server无法正常获取到该Pod对象的状态信息,可能由于无法与所在工作节点的kubelet通信所致(网络通讯不畅)。
      在这里插入图片描述

    3.1 Pod的创建过程

    Pod是Kubernetes中的基础单元,了解它的创建过程对于理解其生命周期有很大的帮助,如下图描述了一个Pod资源对象的典型创建过程。
    在这里插入图片描述

    (1)用户通过kubectl或其他API客户端提交了Pod SpecAPI Server。(create Pod)

    (2)API Server尝试着将Pod对象的相关信息写入etcd存储系统中,待写入操作执行完成,API Server即会返回确认信息至客户端。

    (3)API Server开始反馈etcd中的状态变化。

    (4)所有的kubernetes组件均使用“watch”机制来跟踪检查API Server上的相关的变动。

    (5)kube-scheduler(调度器)通过其“watcher”觉察到API Server创建了新的Pod对象但尚未绑定至任何工作节点。

    (6)kube-schedulerPod对象挑选一个工作节点并将结果信息更新至API Server

    (7)调度结果信息由API Server更新至etcd存储系统,而且API Server也开始反馈此Pod对象的调度结果。

    (8)Pod被调度到的目标工作节点上的kubelet尝试在当前节点上调用Docker启动容器,并将容器的结果状态返回送至API Server

    (9)API ServerPod状态信息存入etcd系统中。

    (10)在etcd确认写入操作成功完成后,API Server将确认信息发送至相关的kubelet,事件将通过它被接受。

    3.2 重要过程

    3.2.1 初始化容器(Init Container)

    初始化容器(init container)是一种专用的容器,用于在启动应用容器(app container)之前启动一个或多个初始化容器,完成应用容器所需的预置条件。
    在这里插入图片描述

    初始化容器与普通的容器非常相似,但它有如下独有特征:

    • 初始化容器总是运行到成功完成为止。
    • 每个初始化容器都必须在下一个初始化容器启动之前成功完成。

    根据Pod的重启策略( restartPolicy ),当init container执行失败,而且设置了 restartPolicy 为Never时,Pod将会启动失败,不再重启;而设置 restartPolicy 为Always时,Pod将会被系统自动重启。

    如果一个Pod指定了多个初始化容器,这些初始化容器会按顺序一次运行一个。只有当前面的初始化容器必须运行成功后,才可以运行下一个初始化容器。当所有的初始化容器运行完成后,Kubernetes才初始化Pod和运行应用容器。

    3.2.2 容器探测

    容器探测(container probe)是Pod对象生命周期中的一项重要的日常任务,它是kubelet对容器周期性执行的健康状态诊断,诊断操作由容器的处理器(handler)进行定义。Kubernetes支持三种处理器用于Pod探测:

    • ExecAction:在容器内执行指定命令,并根据其返回的状态码进行诊断的操作称为Exec探测,状态码为0表示成功,否则即为不健康状态。
    • TCPSocketAction:通过与容器的某TCP端口尝试建立连接进行诊断,端口能够成功打开即为正常,否则为不健康状态。
    • HTTPGetAction:通过向容器IP地址的某指定端口的指定path发起HTTP GET请求进行诊断,响应码为2xx3xx时即为成功,否则为失败。

    任何一种探测方式都可能存在三种结果:“Success”(成功)“Failure”(失败)“Unknown”(未知),只有success表示成功通过检测。

    容器探测分为两种类型:

    • 存活性探测(livenessProbe):用于判定容器是否处于“运行”(Running)状态。一旦此类检测未通过,kubelet将杀死容器并根据重启策略(restartPolicy)决定是否将其重启,未定义存活检测的容器的默认状态为“Success”。
    • 就绪性探测(readinessProbe):用于判断容器是否准备就绪并可对外提供服务。未通过检测的容器意味着其尚未准备就绪,端点控制器(如Service对象)会将其IP从所有匹配到此Pod对象的Service对象的端点列表中移除。检测通过之后,会再将其IP添加至端点列表中。

    什么时候使用存活(liveness)和就绪(readiness)探针?

    如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针,kubelet将根据PodrestartPolicy自动执行正确的操作。

    如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicyAlwaysOnFailure

    如果要仅在探测成功时才开始向Pod发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是spec中的就绪探针的存在意味着Pod将在没有接收到任何流量的情况下启动,并且只有在探针探测成功才开始接收流量。

    如果希望容器能够自行维护,可以指定一个就绪探针,该探针检查与存活探针不同的端点。

    注意:如果只想在Pod被删除时能够排除请求,则不一定需要使用就绪探针;在删除Pod时,Pod会自动将自身置于未完成状态,无论就绪探针是否存在。当等待Pod中的容器停止时,Pod仍处于未完成状态。

    4、Pod调度

    在Kubernetes中,实际我们很少会直接创建一个Pod,在大多数情况下会通过RC(Replication Controller)、Deployment、DaemonSet、Job等控制器来完成对一组Pod副本的创建、调度及全生命周期的自动控制任务。

    在最早的Kubernetes版本里是没有这么多Pod副本控制器的,只有一个Pod副本控制器RC,这个控制器是这样设计实现的:RC独立于所控制的Pod,并通过Label标签这个松耦合关联关系控制目标Pod实例的创建和销毁,随着Kubernetes的发展,RC也出现了新的继任者Deployment,用于更加自动地完成Pod副本的部署、版本更新、回滚等功能。

    严谨地说,RC的继任者其实并不是Deployment,而是ReplicaSet,因为ReplicaSet进一步增强了RC标签选择器的灵活性。之前RC的标签选择器只能选择一个标签,而ReplicaSet拥有集合式的标签选择器,可以选择多个Pod标签,如下所示:

    selector:
      matchLabels:
        tier: frontend
      matchExpressions:
        - {key: tier, operator: In, values: [frontend]}
    

    与RC不同,ReplicaSet被设计成能控制多个不同标签的Pod副本。一种常见的应用场景是,应用APP目前发布了v1与v2两个版本,用户希望APP的Pod副本数保持为3个,可以同时包含v1和v2版本的Pod,就可以用ReplicaSet来实现这种控制,写法如下:

    selector:
      matchLabels:
        version: v2
      matchExpressions:
        - {key: version, operator: In, values: [v1,v2]}
    

    其实,Kubernetes的滚动升级就是巧妙运用ReplicaSet的这个特性来实现的,同时Deployment也是通过ReplicaSet来实现Pod副本自动控制功能的。

    4.1 全自动调度

    Deployment或RC的主要功能之一就是全自动部署一个容器应用的多份副本,以及持续监控副本的数量,在集群内始终维护用户指定的副本数量。

    示例:

    (1)下面以一个Deployment配置的示例,使用这个资源清单配置文件nginx-deployment.yml可以创建一个ReplicaSet,这个ReplicaSet会创建3个Nginx的Pod:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:latest
              ports:
               - name: http
                 containerPort: 80
    

    (2)执行kubectl create命令创建这个Deployment:

    [xcbeyond@localhost ~]$ kubectl create -f nginx-deployment.yml -n test
    deployment.apps/nginx-deployment created
    

    (3)执行kubectl get deployments命令查看Deployment的状态:

    [xcbeyond@localhost ~]$ kubectl get deployments -n test
    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           17m
    

    该结构表明Deployment已经创建好3个副本,并且所有副本都是最新可用的。

    (4)通过执行kubectl get rskubectl get pods命令可以查看已经创建的ReplicaSet(RS)和Pod信息:

    [xcbeyond@localhost ~]$ kubectl get rs -n test
    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-86b8cc866b   3         3         3       24m
    [xcbeyond@localhost ~]$ kubectl get pods -n test
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-86b8cc866b-7rqzt   1/1     Running   0          27m
    nginx-deployment-86b8cc866b-k2rwp   1/1     Running   0          27m
    nginx-deployment-86b8cc866b-rn7l7   1/1     Running   0          27m
    

    从调度策略上来说,这3个Nginx Pod有Kubernetes全自动调度的,它们各自被调度运行在哪个节点上,完全是由Master上的Schedule经过一系列计算得出,用户是无法干预调度过程和调度结果的。

    除了使用自动调度,Kubernetes还提供了多种调度策略供用户实际选择,用户只需在Pod的定义中使用NodeSelectorNodeAffinityPodAffinity、Pod驱逐等更加细粒度的调度策略设置,就能完成对Pod的精准调度。

    4.2 NodeSelector:定向调度

    Kubernetes Master上的Scheduler(kube-scheduler)负责实现Pod的调度,整个调度过程通过执行一系列复杂的算法,最终为每个Pod都计算出一个最佳的目标节点,这一过程是自动完成的,通过我们无法知道Pod最终会被调度到哪个节点上。

    在实际场景下,可能需要将Pod调度到指定的一些Node上,可以通过Node的标签(Label)和Pod的nodeSelector属性相匹配,来达到上述目的。比如希望将MySQL数据库调度到一个SSD磁盘的目标节点上,此时Pod模板中的NodeSelector属性就发挥作用了。

    示例:

    (1)执行kubectl label nodes <node-name> <label-key>=<label-value>命令给指定Node添加标签。

    如,把SSD磁盘的Node添加标签disk-type=ssd

    $ kubectl label nodes k8s-node-1 disk-type=ssd
    

    (2)在Pod的资源清单定义中添加上nodeSelector属性。

    apiVersion: v1
    kind: Pod
    metadata:
      name: mysql
      labels:
        env: test
    spec:
      containers:
      - name: mysql
        image: mysql
      nodeSelector:
        disk-type: ssd
    

    (3)通过执行kubectl get pods -o wide命令查看是否生效。

    除了用户可以给Node添加标签,Kubernetes也给Node预定义了一些标签,方便用户直接使用,预定义的标签有:

    • kubernetes.io/arch:示例,kubernetes.io/arch=amd64。诸如混用 arm 和 x86 节点的情况下很有用。
    • kubernetes.io/os:示例,kubernetes.io/os=linux。在集群中存在不同操作系统的节点时很有用(例如:混合 Linux 和 Windows 操作系统的节点)。
    • beta.kubernetes.io/os (已弃用)
    • beta.kubernetes.io/arch (已弃用)
    • kubernetes.io/hostname:示例,kubernetes.io/hostname=k8s-node-1
    • ……

    更多可参考:https://kubernetes.io/docs/reference/kubernetes-api/labels-annotations-taints/

    NodeSelector通过标签的方式,简单实现了限制Pod所在节点的方法,看似既简单又完美,但在真实环境中可能面临以下令人尴尬的问题:

    (1)如果NodeSelector选择的Label不存在或者不符合条件,比如这些目标节点宕机或者资源不足时,该怎么办?

    (2)如果要选择多种合适的目标节点,比如SSD磁盘的节点或者超高速硬盘的节点,该怎么办?Kubernetes引入了NodeAffinity(节点亲和性)来解决上述问题。

    (3)不同Pod之间的亲和性(Affinity)。比如MySQL和Redis不能被调度到同一个目标节点上,或者两种不同的Pod必须调度到同一个Node上,以实现本地文件共享或本地网络通信等特殊要求,这就是PodAffinity要解决的问题。

    4.3 NodeAffinity:Node亲和性调度

    NodeAffinity,是Node亲和性的调度策略,是用于解决NodeSelector不足的一种全新调度策略。

    目前有两种方式来表达:

    • RequiredDuringSchuedlingIgnoredDuringExecution必须满足指定的规则才可以调度Pod到Node上(功能与nodeSelector很像,但使用的是不同语法),相当于硬限制。
    • PreferredDuringSchedulingIgnoredDuringExection:强调优先满足指定规则,调度器会尝试调度Pod到Node上,但并不强求,相当于软限制。多个优先级规则还可以设置权重值,以定义执行的先后顺序。

    lgnoredDuringExecution隐含的意思是,在Pod资源基于Node亲和性规则调度至某节点之后,节点标签发生了改变而不再符合此节点亲和性规则时 ,调度器不会将Pod对象从此节点上移出。因此,NodeAffinity仅对新建的Pod对象生效

    Node亲和性可通过pod.spec.affinity.nodeAffinity进行指定。

    示例:

    设置NodeAffinity调度规则如下:

    • requiredDuringSchedulingIgnoredDuringExecution要求只运行在amd64的节点上(kubernetes.io/arch In amd64)。
    • preferredDuringSchedulingIgnoredDuringExecution要求尽量运行在磁盘类型为ssd的节点上(disk-type In ssd)。

    pod-with-node-affinity.yml定义如下:

    apiVersion: v1
    kind: Pod
    metadata:
      name: with-node-affinity
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/arch
                operator: In
                values:
                - amd64
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            preference:
              matchExpressions:
              - key: disk-type
                operator: In
                values:
                - ssd
      containers:
      - name: with-node-affinity
        image: busybox
    

    从上面的示例中看到使用了In操作符。NodeAffinity语法支持的操作符包括: InNotInExistsDoesNotExistGtLt。 你可以使用 NotInDoesNotExist 来实现节点反亲和行为,即实现排查的功能。

    NodeAffinity规则定义的注意事项如下:

    • 如果同时指定了 nodeSelectornodeAffinity,那么两者必须都要满足,才能将Pod调度到指定的节点上。
    • 如果 nodeAffinity 指定了多个 nodeSelectorTerms,那么其中一个能够匹配成功即可。
    • 如果在nodeSelectorTerms 中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。

    如果你指定了多个与 nodeSelectorTerms 关联的 matchExpressions

    4.4 PodAffinity:Pod亲和和互斥调度策略

    Pod间的亲和与互斥从Kubernetes 1.4版本开始引入的。这一功能让用户从另一个角度来限制Pod所能运行的节点:根据在节点上正在运行的Pod标签而不是节点的标签进行判断和调度,要求对节点和Pod两个条件进行匹配。这种规则可以描述为:如果在具有标签X的Node上运行了一个或者多个符合条件Y的Pod,那么Pod应该运行在这个Node上。

    NodeAffinity不同的是,Pod是属于某个命名空间的,所以条件Y表达的是一个或者全部命名空间中的一个Label Selector。

    NodeAffinity相同,Pod亲和和互斥的设置也是requiredDuringSchedulingIgnoredDuringExecutionPreferredDuringSchedulingIgnoredDuringExection

    Pod亲和性可通过pod.spec.affinity.podAffinity进行指定。

    4.5 Taints和Tolerations(污点和容忍)

    NodeAffinity节点亲和性,是Pod中定义的一种属性,使得Pod能够被调度到某些Node上运行。而Taints则正好相反,它让Node拒绝Pod的运行。

    Taints需要和Toleration配合使用,让Pod避开一些不合适的Node。在Node上设置一个或多个Taint之后,除非Pod明确声明能够容忍这些污点,否则无法在这些Node上运行。Toleration是Pod的属性,让Pod能够运行在标注了Taint的Node上。

    可使用kubectl taint命令为Node设置Taint信息:

    [xcbeyond@localhost ~]$ kubectl taint nodes node1 key=value:NoSchedule
    

    4.6 Pod优先级调度

    在Kubernetes 1.8版本引入了基于Pod优先级抢占(Pod priority Preemption)的调度策略,此时Kubernetes会尝试释放目标节点上优先级低的Pod,以腾出资源空间来安置优先级高的Pod,这种调度方式被称为”抢占式调度“。

    可以通过以下几个维度来定义:

    • Priority:优先级
    • QoS:服务质量等级
    • 系统定义的其他度量指标

    示例:

    (1)创建PriorityClasses,不属于任何命名空间:

    apiVersion: scheduling.k8s.io/v1beta1
    kind: PriorityClass
    metadata:
      name: high-priority
      value: 1000000
      globalDefault: false
      description: "this priority class should be used for XYZ service pods only."
    

    说明:定义名为high-priority的优先级,优先级为1000000,数字越大,优先级越高。优先级数字大于一亿的数字被系统保留,用于指派给系统组件。

    (2)在Pod中引用上述定义的Pod优先级:

    apiVersion: v1
    kind: Pod
    metadata:
      name: frontend
      namespace: test
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: xcbeyond/vue-frontend:latest
        ports:
          - name: port
            containerPort: 80
            hostPort: 8080
        priorityClassName: high-priority  # 引用Pod优先级
    

    使用优先级抢占的调度策略可能会导致某些Pod永远无法被成功调度。优先级调度不但增加了系统的复杂性,还可能带来额外不稳定的因素。因此,一旦发生资源紧张的局面,首先要考虑的是集群扩容,如果无法扩容,则再考虑有监管的优先级调度特性,比如结合基于Namespace的资源配额限制来约束任意优先级抢占行为。

    4.7 DaemonSet:在每个Node上都调度一个Pod

    DaemonSet是Kubernetes 1.2版本新增的一种资源对象,用于管理在集群中每个Node上仅运行一份Pod的副本实例。如下图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3UJhzRij-1611625546431)(./DaemonSet示例.jpg)]

    DaemonSet的一些典型用法:

    • 在每个节点上运行集群守护进程。如:运行一个GlusterFS存储或Ceph存储的Daemon进程。
    • 在每个节点上运行日志收集守护进程。如:运行一个日志采集程序,Fluentd或Logstach。
    • 在每个节点上运行监控守护进程。如:采集该Node的运行性能数据,Prometheus Node Exporter、collectd等。

    4.8 Job:批处理调度

    Job负责批量处理短暂的一次性任务 ,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。

    按照批处理任务实现方式的不同,有以下几种典型模式适用于不同的业务场景:

    • 基于Job模版进行扩展:

      需要先编写一个通用的Job模版,根据不同的参数生成多个Job json/yml文件用于Job的创建,可以使用相同的标签进行Job管理。

    • 按每个工作项目排列的队列:

      • 需要用户提前准备好一个消息队列服务,比如rabbitMQ,该服务是一个公共组件,每个工作项目可以往里塞任务消息。
      • 用户可以创建并行Job,需要能适用于该消息队列,然后从该消息队列中消费任务,并进行处理直到消息被处理完。
      • 该模式下,用户需要根据项目数量填写spec.completions, 并行数量.spec.parallelism可以根据实际情况填写。该模式下就是以所有的任务都成功完成了,job才会成功结束。
    • 可变任务数量的队列:

      • 需要用户提前准备好一个存储服务来保存工作队列,比如Redis。每个项目可以往该存储服务填充消息。
      • 用户可以启动适用于该工作队列的多个并行Job,进行消息处理。与前一个Rabbit消息队列的差异在于,每个Job任务是可以知道工作队列已经空了,这时候便可以成功退出。
      • 该模式下,spec.completions需要置1, 并行数量.spec.parallelism可以根据实际情况填写。只要其中有一个任务成功完成,该Job就会成功结束。
    • 普通的静态任务:

      采用静态方式分配任务项。

    考虑到批处理的并行问题,Kubernetes将Job分为以下三种类型:

    • 非并行Job:

      通常一个Job只运行一个Pod,一旦此Pod正常结束,Job将结束。(Pod异常,则会重启)

    • 固定完成次数的并行Job:

      并发运行指定数量的Pod,直到指定数量的Pod成功,Job结束。

    • 带有工作队列的并行Job:

      • 用户可以指定并行的Pod数量,当任何Pod成功结束后,不会再创建新的Pod。
      • 一旦有一个Pod成功结束,并且所有的Pods都结束了,该Job就成功结束。
      • 一旦有一个Pod成功结束,其他Pods都会准备退出。

    4.9 Cronjob:定时任务

    类似Linux中的Cron的定时任务CronJob。

    定时表达式,格式如下:

    Minutes Hours DayofMonth Month DayofWeek Year

    • Minutes:分钟,有效范围为0-59的整数。可以为, - * /这4个字符。

    • Hours:小时,有效范围为0-23的整数。可以为, - * /这4个字符。

    • DayofMonth:每月的哪一天,有效范围为0-31的整数。可以为, - * / ? L W C这8个字符。

    • Month:月份,有效范围为1-12的整数或JAN-DEC。可以为, - * /这4个字符。

    • DayofWeek:星期,有效范围为1-7的整数或SUN-SAT,1表示星期天,2表示星期一,以此类推。可以为, - * / ? L C #这8个字符。

    例如,每隔1分钟执行一次任务,则Cron表达式为*/1 * * * *

    示例:

    (1)定义CronJob的资源配置文件test-cronjob.yml

    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: test
    spec:
      schedule: "*/1 * * * *"
      jobTemplate:
        spec:
          template:
            spec:
              containers:
                - name: hello
                  image: busybox
                  args:
                    - /bin/sh
                    - -c
                    - date; echo hello from the Kubernetes cluster
              restartPolicy: OnFailure
    

    (2)执行kubectl crate命令创建CronJob:

    [xcbeyond@localhost k8s]$ kubectl create -f test-cronjob.yml -n test
    cronjob.batch/test created
    

    (3)每隔1分钟执行kubectl get cronjob命令查看任务状态,发现的确是每分钟调度一次:

    [xcbeyond@localhost k8s]$ kubectl get cronjob test -n test
    NAME   SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
    test   */1 * * * *   False     1        61s             69s
    [xcbeyond@localhost k8s]$ kubectl get cronjob test -n test
    NAME   SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
    test   */1 * * * *   False     2        7s              75s
    [xcbeyond@localhost k8s]$ kubectl get cronjob test -n test
    NAME   SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
    test   */1 * * * *   False     3        28s             2m36s
    

    执行kubectl delete cronjob命令进行删除。

    4.10 自定义调度器

    如果在上述的调度器还是无法满足一些独特的需求,还可以使用任何语言实现简单或复杂的自定义调度器。

    5、Pod升级和回滚

    当需要升级某个服务时,一般会停止与该服务相关的所有Pod,然后下载新版本镜像并创建新的Pod。如果集群规模较大,则这样的工作就变成了一个挑战,并且长时间的服务不可用,也是很难让用户接受的。

    为了解决上述问题,Kubernetes提供了滚动升级能够很好的解决。

    如果Pod是通过Deployment创建,则可以在运行时修改Deployment的Pod定义(spc.template)或镜像名称,并应用到Deployment对象上,系统即可完成Deployment的自动更新操作。如果在更新过程中发生了错误,则可以通过回滚操作恢复Pod的版本。

    5.1 Deployment的升级

    nginx-deployment.yml为例:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:latest
              ports:
               - name: http
                 containerPort: 80
    

    (1)已运行的Pod副本数量为3,查看Pod状态:

    [xcbeyond@localhost k8s]$ kubectl get pod -n test
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-86b8cc866b-7rqzt   1/1     Running   2          53d
    nginx-deployment-86b8cc866b-k2rwp   1/1     Running   2          53d
    nginx-deployment-86b8cc866b-rn7l7   1/1     Running   2          53d
    

    (2)现将nginx版本更新为nginx:1.9.1,可通过执行kubectl set image命令为Deployment设置新的镜像:

    [xcbeyond@localhost k8s]$ kubectl set image deployment/nginx-deployment  nginx=nginx:1.9.1 -n test
    deployment.apps/nginx-deployment image updated
    

    kubectl set image:更新现有的资源对象的容器镜像。

    另一种升级方法是使用kubectl edit命令修改Deployment的配置。

    (3)此时(升级过程中),查看Pod状态,发现正在进行升级:

    [xcbeyond@localhost k8s]$ kubectl get pod -n test
    NAME                                READY   STATUS              RESTARTS   AGE
    nginx-deployment-79fbf694f6-kplgz   0/1     ContainerCreating   0          96s
    nginx-deployment-86b8cc866b-7rqzt   1/1     Running             2          53d
    nginx-deployment-86b8cc866b-k2rwp   1/1     Running             2          53d
    nginx-deployment-86b8cc866b-rn7l7   1/1     Running             2          53d
    

    升级过程中,可以通过执行kubectl rollout status命令查看Deployment的更新过程。

    (4)升级完成后,查看Pod状态:

    [xcbeyond@localhost k8s]$ kubectl get pod -n test
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-79fbf694f6-h7nfs   1/1     Running   0          2m43s
    nginx-deployment-79fbf694f6-kplgz   1/1     Running   0          7m21s
    nginx-deployment-79fbf694f6-txrfj   1/1     Running   0          2m57s
    

    对比升级完成前后Pod状态列表中的NAME,已经发生了变化。

    查看Pod使用的镜像,已经更新为nginx:1.9.1:

    [xcbeyond@localhost k8s]$ kubectl describe pod/nginx-deployment-79fbf694f6-h7nfs -n test
    Name:         nginx-deployment-79fbf694f6-h7nfs
    Namespace:    test
    ……
    Containers:
      nginx:
        Container ID:   docker://0ffd43455aa3a147ca0795cf58c68da63726a3c77b40d58bfa5084fb879451d5
        Image:          nginx:1.9.1
        Image ID:       docker-pullable://nginx@sha256:2f68b99bc0d6d25d0c56876b924ec20418544ff28e1fb89a4c27679a40da811b
        Port:           80/TCP
    ……
    

    那么Deployment是如何完成Pod更新的呢?

    使用kubectl describe deployment/nginx-deployment命令仔细查看Deployment的更新过程:

    初始创建Deployment时,系统创建了一个ReplicaSet(nginx-deployment-86b8cc866b),并创建了3个Pod副本。当更新Deployment时,系统创建了一个新的ReplicaSet(nginx-deployment-79fbf694f6),并将其副本数量扩展到1,然后将旧的ReplicaSet缩减为2。之后,系统继续按照相同的更新策略对新旧两个ReplicaSet进行逐个调整。最后,新的ReplicaSet运行了3个新版本的Pod副本,旧的ReplicaSet副本数量则缩减为0。

    如下图所示:
    在这里插入图片描述

    执行kubectl describe deployment/nginx-deployment命令,查看Deployment的最终信息:

    [xcbeyond@localhost k8s]$ kubectl describe deployment/nginx-deployment -n test
    Name:                   nginx-deployment
    Namespace:              test
    CreationTimestamp:      Thu, 26 Nov 2020 19:32:04 +0800
    Labels:                 <none>
    Annotations:            deployment.kubernetes.io/revision: 2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.9.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-79fbf694f6 (3/3 replicas created)
    Events:
      Type    Reason             Age   From                   Message
      ----    ------             ----  ----                   -------
      Normal  ScalingReplicaSet  30m   deployment-controller  Scaled up replica set nginx-deployment-79fbf694f6 to 1
      Normal  ScalingReplicaSet  25m   deployment-controller  Scaled down replica set nginx-deployment-86b8cc866b to 2
      Normal  ScalingReplicaSet  25m   deployment-controller  Scaled up replica set nginx-deployment-79fbf694f6 to 2
      Normal  ScalingReplicaSet  25m   deployment-controller  Scaled down replica set nginx-deployment-86b8cc866b to 1
      Normal  ScalingReplicaSet  25m   deployment-controller  Scaled up replica set nginx-deployment-79fbf694f6 to 3
      Normal  ScalingReplicaSet  24m   deployment-controller  Scaled down replica set nginx-deployment-86b8cc866b to 0
    

    执行kubectl get rs命令,查看两个ReplicaSet的最终状态:

    [xcbeyond@localhost k8s]$ kubectl get rs -n test
    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-79fbf694f6   3         3         3       39m
    nginx-deployment-86b8cc866b   0         0         0       53d
    

    在整个升级过程中,系统会保证至少有两个Pod可用,并且最多同时运行4个Pod,这是Deployment通过复杂的算法完成。Deployment需要确保在整个更新过程中只有一定数量的Pod可能处于不可用状态。在默认情况下,Deployment确保可用的Pod总数至少所需的副本数量(desired)减1,也就是最多1个不可用(maxUnavailable=1)。

    这样,在升级过程中,Deployment就能够保证服务不中断,并且副本数量始终维持为指定的数量(desired)。

    更新策略

    在Deployment的定义中,可以通过spec.strategy指定Pod更新策略。目前支持两种策略:Recreate(重建)和RollingUpdate(滚动更新),默认值为RollingUpdate。

    • **Recreate:**设置spec.strategy.type=Recrate,表示Deployment在更新Pod时,会先杀掉所有正在运行的Pod,然后创建新的Pod。
    • **RollingUpdate:**设置spec.strategy.type=RollingUpdate,表示Deployment会以滚动更新的方式来逐个更新Pod。同时,可通过设置spec.strategy.rollingUpdate中的两个参数maxUnavailablemaxSurge来控制滚动更新的过程。
      • spec.strategy.rollingUpdate.maxUnavailable:用于指定Deployment在更新过程中最大不可用状态的Pod数量。 该值可以是具体数字,或者Pod期望副本数的百分比。
      • spec.strategy.rollingUpdate.maxSurge:用于指定在Deployment更新Pod的过程中Pod总数超过Pod期望副本数部分的最大值。该值可以是具体数字,或者Pod期望副本数的百分比。

    5.2 Deployment的回滚

    在默认情况下,所有Deployment的发布历史记录都被保留在系统中,以便于我们随时进行回滚。(历史记录数量可配置)

    可通过执行kubectl rollout undo命令完成Deployment的回滚。

    (1)执行kubectl rollout history命令检查某个Deployment部署的历史记录:

    [xcbeyond@localhost k8s]$ kubectl rollout history deployment/nginx-deployment -n test
    deployment.apps/nginx-deployment 
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none
    

    在创建Deployment时使用--record参数,就可以在CHANGE-CAUSE列看到每个版本创建/更新的命令。

    (2)如果需要查看指定版本的详细信息,则可加上--revision=<N>参数:

    [xcbeyond@localhost k8s]$ kubectl rollout history deployment/nginx-deployment --revision=2 -n test
    deployment.apps/nginx-deployment with revision #2
    Pod Template:
      Labels:	app=nginx
    	pod-template-hash=79fbf694f6
      Containers:
       nginx:
        Image:	nginx:1.9.1
        Port:	80/TCP
        Host Port:	0/TCP
        Environment:	<none>
        Mounts:	<none>
      Volumes:	<none>
    

    (3)先将撤销本次升级版本,并回滚到上一版本,即:nginx:1.9.1-> nginx:latest。执行kubectl rollout undo命令:

    [xcbeyond@localhost k8s]$ kubectl rollout undo deployment/nginx-deployment -n test
    deployment.apps/nginx-deployment rolled back
    

    当然,也可以使用–to-revision参数指定回滚到具体某个版本号。

    (4)可执行kubectl describe deployment/nginx-deployment命令查看回滚的整个过程:

    [xcbeyond@localhost k8s]$ kubectl describe deployment/nginx-deployment -n test
    Name:                   nginx-deployment
    Namespace:              test
    CreationTimestamp:      Thu, 26 Nov 2020 19:32:04 +0800
    Labels:                 <none>
    Annotations:            deployment.kubernetes.io/revision: 3
    Selector:               app=nginx
    Replicas:               3 desired | 2 updated | 4 total | 3 available | 1 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:latest
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:  nginx-deployment-79fbf694f6 (2/2 replicas created)
    NewReplicaSet:   nginx-deployment-86b8cc866b (2/2 replicas created)
    Events:
      Type    Reason             Age    From                   Message
      ----    ------             ----   ----                   -------
      Normal  ScalingReplicaSet  5m50s  deployment-controller  Scaled up replica set nginx-deployment-86b8cc866b to 1
      Normal  ScalingReplicaSet  4m55s  deployment-controller  Scaled down replica set nginx-deployment-79fbf694f6 to 2
      Normal  ScalingReplicaSet  4m55s  deployment-controller  Scaled up replica set nginx-deployment-86b8cc866b to 2
    

    5.3 RC的滚动升级

    对于RC的滚动升级,Kubernetes还提供了一个kubectl rolling-update命令实现。该命令创建一个新的RC,然后自动控制旧的RC中的Pod副本数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,来完成Pod的升级。

    5.4 其他对象的更新策略

    Kubernetes从1.6版本开始,对DaemonSet和StatefulSet的更新策略也引入类似于Deployment的滚动升级,通过不同的策略自动完成应用的版本升级。

    5.4.1 DaemonSet的更新策略

    DaemonSet的升级策略包括两种:

    • OnDelete:DaemonSet的默认策略。当使用OnDelete策略对DaemonSet进行更新时,在创建好新的DaemonSet配置之后,新的Pod并不会被自动创建,直到用户手动删除旧版本的Pod,才会触发新建操作。
    • RollingUpdate:当使用RollingUpdate策略对DaemonSet进行更新时,旧版本的Pod将被自动删除,然后自动创建新版本的Pod。

    5.4.2 StatefulSet的更新策略

    Kubernetes从1.6版本开始,针对StatefulSet的更新策略逐渐向Deployment和DaemonSet的更新策略看齐,也将实现RollingUpdate、Partioned和onDelete这些策略,以保证StatefulSet中Pod有序地、逐个地更新,并保留更新历史记录,也能够回滚到某个历史版本。

    6、Pod扩容

    在实际生产环境下,我们面对不同场景,可能会进行服务实例的调整(增加或减少),以确保能够充分利用好系统资源。此时,可以利用Deployment/RC的Scale机制来完成这些工作。

    Kubernetes对Pod扩容提供了手动和自动两种模式:

    • **手动模式:**通过执行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置。
    • **自动模式:**用户根据某个性能指标或自定义业务指标,并指定Pod副本数量的范围,系统将自动在这个范围内根据性能指标的变化进行调整。

    6.1 手动模式扩容

    nginx-deployment.yml为例:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-deployment
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
            - name: nginx
              image: nginx:latest
              ports:
               - name: http
                 containerPort: 80
    

    (1)已运行的Pod副本数量为3,查看Pod状态:

    [xcbeyond@localhost ~]$ kubectl get pod -n test
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-86b8cc866b-7g4xp   1/1     Running   0          24h
    nginx-deployment-86b8cc866b-pgh6c   1/1     Running   0          24h
    nginx-deployment-86b8cc866b-qg26j   1/1     Running   0          23h
    

    (2)通过执行kubectl scale命令将Pod副本数量从初始的3个更新为5个:

    [xcbeyond@localhost ~]$ kubectl scale deployment nginx-deployment --replicas 5 -n test
    deployment.apps/nginx-deployment scaled
    [xcbeyond@localhost ~]$ kubectl get pod -n test
    NAME                                READY   STATUS              RESTARTS   AGE
    nginx-deployment-86b8cc866b-7g4xp   1/1     Running             0          24h
    nginx-deployment-86b8cc866b-dbkms   0/1     ContainerCreating   0          5s
    nginx-deployment-86b8cc866b-pgh6c   1/1     Running             0          24h
    nginx-deployment-86b8cc866b-qg26j   1/1     Running             0          23h
    nginx-deployment-86b8cc866b-xv5pm   0/1     ContainerCreating   0          5s
    [xcbeyond@localhost ~]$ kubectl get pod -n test
    NAME                                READY   STATUS    RESTARTS   AGE
    nginx-deployment-86b8cc866b-7g4xp   1/1     Running   0          24h
    nginx-deployment-86b8cc866b-dbkms   1/1     Running   0          79s
    nginx-deployment-86b8cc866b-pgh6c   1/1     Running   0          24h
    nginx-deployment-86b8cc866b-qg26j   1/1     Running   0          23h
    nginx-deployment-86b8cc866b-xv5pm   1/1     Running   0          79s
    

    如果–replicas设置为比当前Pod副本小,则会删除一些运行中的Pod,以实现缩容。

    6.2 自动模式扩容

    Kubernetes从1.1版本开始,新增了Pod 横向自动扩容的功能(Horizontal Pod Autoscaler,简称 HPA),用于实现基于CPU使用率进行自动Pod扩容的功能。

    HPA 与 Deployment、Service 一样,也属于一种Kubernetes资源对象。

    HPA 的目标是通过追踪集群中所有 Pod 的负载变化情况(基于Master的kube-controller-manager服务持续监测目标Pod的某种性能指标),来自动化地调整Pod的副本数,以此来满足应用的需求和减少资源的浪费。

    目前Kubernetes支持的指标类型如下:

    • Pod资源使用率: Pod级别的性能指标,通常是一个比率指,例如CPU 利用率。
    • Pod自定义指标: Pod级别的性能指标,通常是一个数值,例如服务在每秒之内的请求数(TPS 或 QPS)。
    • Object自定义指标或外部自定义指标: 通常是一个数值,需要容器应用以某种方式提供,例如通过HTTP URL “/metrics”提供,或使用外部服务提供的指标采集URL(如,某个业务指标)。

    如何统计和查询这些指标呢?

    Kubernetes从1.11版本开始,弃用基于Heapster组件完成Pod的CPU使用率采集的机制,全面转向基于Metrics Server完成数据采集。Metrics Server将采集到的Pod性能指标数据通过聚合API(如,metrics.k8s.io、custom.metrics.k8s.io和external.metrics.k8s.io)提供给HPA控制器进行查询。

    6.2.1 HPA的工作原理

    Kubernetes中的某个Metrics Server(Heapster或自定义Metrics Server)持续采集所有Pod副本的指标数据。HPA控制器通过Metrics Server的API获取这些数据,基于用户定义的扩容规则进行计算,得到目标Pod副本数量。当目标Pod副本数量与当前副本数量不同时,HPA控制器就向Pod的副本控制器(Deployment、RC或ReplicaSet)发起scale操作(等同于手动模式中执行kubectl scale命令),调整Pod的副本数量,完成扩容操作。

    如下图描述了HPA体系中的关键组件和工作流程:
    在这里插入图片描述

    HPA控制器是基于Master的kube-controller-manager服务启动参数--horizontal-pod-autoscaler-sync-period定义的探测周期(默认值为15s)。

    6.2.2 HPA配置说明

    自动模式扩容是通过HorizontalPodAutoscaler资源对象提供给用户来定义扩容规则。

    HorizontalPodAutoscaler资源对象位于Kubernetes的API组“autoscaling”中,目前包括v1和v2两个版本。

    • autoscaling/v1:仅支持基于CPU使用率的自动扩容。
    • autoscaling/v2*:支持基于任意指标的自动扩容配置,包括基于资源使用率、Pod指标、其他指标等类型的指标数据。当前版本为autoscaling/v2beta2。

    下面对HorizontalPodAutoscaler的配置和用法进行具体说明。

    详细配置项可参考:

    1. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#horizontalpodautoscaler-v1-autoscaling

    2. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#horizontalpodautoscaler-v2beta2-autoscaling

    (1)基于autoscaling/v1版本的HorizontalPodAutoscaler配置:

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
      name: nginx
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      minReplicas: 1
      maxReplicas: 10
      targetCPUUtilizationPercentage: 50
    

    参数说明:

    • scaleTargetRef:目标作用对象,可以是Deployment、RC、ReplicaSet。
    • targetCPUUtilizationPercentage:期望每个Pod的CPU使用率。
    • minReplicasmaxReplicas:P大副本数量的最小值和最大值,系统将在这个范围内进行自动扩容操作,并维持每个Pod的CPU使用率为设置值。

    使用autoscaling/v1版本的HorizontalPodAutoscaler,需预先安装Heapster组件或Metrics Server,用于采集Pod的CPU使用率。

    (2)基于autoscaling/v2beta2版本的HorizontalPodAutoscaler配置:

    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    metadata:
      name: nginx
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      minReplicas: 1
      maxReplicas: 10
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 50
    

    参数说明:

    • scaleTargetRef:目标作用对象,可以是Deployment、RC、ReplicaSet。
    • minReplicasmaxReplicas:P大副本数量的最小值和最大值,系统将在这个范围内进行自动扩容操作,并维持每个Pod的CPU使用率为设置值。
    • metrics:目标指标值。
      • type:定义指标的类型。可设置四种类型,支持设置一个或多个类型的组合:
        • Resource:基于资源的指标值,如CPU和内存。对应CPU使用率,在target参数中设置averageUtilization定义目标平均CPU使用率;对应内存资源,在target参数中设置AverageValue定义目标平均内存使用值。
        • Pods:基于Pod的指标,系统将对全部Pod副本的指标值进行平均值计算。其target指标类型只能使用AverageValue。指标的数据通常需要搭建自定义Metrics Server和监控工具进行采集和处理。
        • Object:基于某种资源对象的指标或应用系统的任意自定义指标。指标的数据通常需要搭建自定义Metrics Server和监控工具进行采集和处理。
        • External:Kubernetes从1.10版本开始,引入了对外部系统指标的支持。例如,用户使用了公有云服务商提供的消息服务或外部负载均衡,可以基于这些外部服务的性能指标对自己部署在Kubernetes中的服务进行自动扩容操作。
      • target:定义相应的指标目标值,系统将在指标数据达到目标值时触发扩容操作。

    例1,设置指标的名称为requests-per-second,其值来源于Ingress “main-route”,将目标值(value)设置为2000,即在Ingress的每秒请求数量达到2000个时触发扩容操作:

    metrics:
    - type: Object
      object:
        metric:
          name: requests-per-second
        describedObject:
          apiVersion: extensions/v1beta1
          kind: Ingress
          name: main-route
        target:
          type: Value
          value: 2k
    

    例2,设置指标的名称为http_requests,并且该资源对象具有标签“verb=GET”,在指标平均值达到500时触发扩容操作:

    metrics:
    - type: Object
      object:
        metric:
          name: http_requests
          selector: 'verb=GET'
        target:
          type: AverageValue
          averageValue: 500
    

    还可以在同一个HorizontalPodAutoscaler资源对象中定义多个类型的指标,系统将针对每种类型的指标都计算Pod副本的目标数量,以最大值为准进行扩容操作。例如:

    apiVersion: autoscaling/v2beta2
    kind: HorizontalPodAutoscaler
    metadata:
      name: nginx
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: nginx
      minReplicas: 1
      maxReplicas: 10
      metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: AverageUtilization
            averageUtilization: 50
      - type: Pods
        pods:
          metric:
            name: packets-per-second
          target:
            type: AverageValue
            averageValue: 1k
      - type: Object
        object:
          metric:
            name: requests-per-second
          describedObject:
            apiVersion: extensions/v1beta1
            kind: Ingress
          target:
            type: Value
            value: 10k
    

    例3,设置指标的名称为queue_messages_ready,具有queue=worker_tasks标签在目标指标平均值为30时触发自动扩容操作:

    metrics:
    - type: External
      external:
        metric:
          name: queue_messages_ready
          selector: 'queue=worker_tasks'
        target:
          type: AverageValue
          averageValue: 30
    

    在使用外部服务的指标时,需安装、部署能够对接到Kubernetes HPA模型的监控系统,并且完成了解监控系统采集这些指标的机制,后续的自动扩容操作才能完成。

    6.2.3 基于自定义指标的HPA实践

    下面通过一个完整的示例,对如何搭建和使用基于自定义指标的HPA体系进行说明。

    基于自定义指标进行自动扩容时,需预先部署自定义Metrics Server,目前可以使用基于Prometheus、Microsoft Azure、Google Stackdriver等系统的Adapter实现自定义Metrics Server。自定义Metrice Server可参考https://github.com/kubernetes/metrics/blob/master/IMPLEMENTATIONS.md#custom-metrics-api的说明。

    本节是基于Prometheus监控系统对HPA的基础组件部署和HPA配置进行详细说明。

    基于Prometheus的HPA架构如下图所示:
    在这里插入图片描述

    关键组件说明如下:

    • Prometheus: 是一个开源的服务监控系统,用于定期采集各Pod的性能指标数据。
    • Custom Metrics Server: 自定义Metrics Server,用Prometheus Adapter进行具体实现。它从Prometheus服务采集性能指标数据,通过Kubernetes的Metrics Aggregation层将自定义指标API注册到Master的API Server中以/apis/custom.metrics.k8s.io路径提供指标数据。
    • HPA Controller: Kubernetes的HPA控制器,基于用户自定义的HorizontalPodAutoscaler进行自动扩容操作。

    整个部署过程如下:

    (1)开启kube-apiserverkube-controller-manager服务的相关启动参数。

    kube-apiserverkube-controller-manager服务默认已经部署在kube-system命名空间。

    [xcbeyond@localhost minikube]$ kubectl get pod -n kube-system
    NAME                               READY   STATUS    RESTARTS   AGE
    coredns-6c76c8bb89-p26xx           1/1     Running   11         103d
    etcd-minikube                      1/1     Running   11         103d
    kube-apiserver-minikube            1/1     Running   11         103d
    kube-controller-manager-minikube   1/1     Running   11         103d
    kube-proxy-gcd8d                   1/1     Running   11         103d
    kube-scheduler-minikube            1/1     Running   11         103d
    storage-provisioner                1/1     Running   29         103d
    

    注:本Kubernetes环境是基于Minikube方式部署的本地环境。

    可通过kubectl describe命令查看服务目前的启动参数情况,例如:

    [xcbeyond@localhost minikube]$ kubectl describe pod/kube-apiserver-minikube -n kube-system
    Name:                 kube-apiserver-minikube
    Namespace:            kube-system
    ……
    Containers:
    kube-apiserver:
    ……
     Command:
       kube-apiserver
       --advertise-address=172.17.0.2
       --allow-privileged=true
       --authorization-mode=Node,RBAC
       --client-ca-file=/var/lib/minikube/certs/ca.crt
       --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
       --enable-bootstrap-token-auth=true
       --etcd-cafile=/var/lib/minikube/certs/etcd/ca.crt
       --etcd-certfile=/var/lib/minikube/certs/apiserver-etcd-client.crt
       --etcd-keyfile=/var/lib/minikube/certs/apiserver-etcd-client.key
       --etcd-servers=https://127.0.0.1:2379
       --insecure-port=0
       --kubelet-client-certificate=/var/lib/minikube/certs/apiserver-kubelet-client.crt
       --kubelet-client-key=/var/lib/minikube/certs/apiserver-kubelet-client.key
       --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
       --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt
       --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key
       --requestheader-allowed-names=front-proxy-client
       --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt
       --requestheader-extra-headers-prefix=X-Remote-Extra-
       --requestheader-group-headers=X-Remote-Group
       --requestheader-username-headers=X-Remote-User
       --secure-port=8443
       --service-account-key-file=/var/lib/minikube/certs/sa.pub
       --service-cluster-ip-range=10.96.0.0/12
       --tls-cert-file=/var/lib/minikube/certs/apiserver.crt
       --tls-private-key-file=/var/lib/minikube/certs/apiserver.key
    ……
    

    可通过kubectl edit命令对服务启动参数进行更新,如:

    [xcbeyond@localhost minikube]$ kubectl edit pod kube-apiserver-minikube -n kube-system
    

    在Master的API Server启动Aggregation层,通过设置kube-apiserver服务的下列启动参数进行开启。

    • --requestheader-client-ca-file=/var/lib/minikube/certs/front-proxy-ca.crt:客户端CA证书。
    • --requestheader-allowed-names=front-proxy-client:允许访问的客户端common names列表,通过header中由--requestheader-username-headers参数指定的字段获取。客户端common names的名称需要在client-ca-file中进行配置,将其设置为空值时,表示任意客户端都可以访问。
    • --requestheader-extra-headers-prefix=X-Remote-Extra-:请求头中需要坚持的前缀名。
    • --requestheader-group-headers=X-Remote-Group:请求头中需要检查的组名。
    • --requestheader-username-headers=X-Remote-User:请求头中需要检查的用户名。
    • --proxy-client-cert-file=/var/lib/minikube/certs/front-proxy-client.crt:在请求期间验证Aggregator的客户端CA证书。
    • --proxy-client-key-file=/var/lib/minikube/certs/front-proxy-client.key:在请求期间验证Aggregator的客户端私钥。

    配置kube-controller-manager服务中HPA的相关启动参数(可选配置)如下:

    • --horizontal-pod-autoscaler-sync-period=10s:HPA控制器同步Pod副本数量的空间间隔,默认值为15s。
    • --horizontal-pod-autoscaler-downscale-stabilization=1m:执行扩容操作的等待时长,默认值为5min。
    • --horizontal-pod-autoscaler-initial-readiness-delay=30s:等待Pod达到Read状态的时延,默认值为30min。
    • --horizontal-pod-autoscaler-tolerance=0.1:扩容计算结果的容忍度,默认值为0.1,表示[-10% - +10%]。

    (2)部署Prometheus。

    这里使用Prometheus-operator来部署。

    Prometheus Operator为监控 Kubernetes service、deployment和Prometheus实例的管理提供了简单的定义,简化在Kubernetes上部署、管理和运行。

    (3)部署自定义Metrics Server。

    以Prometheus Adapter的实现进行部署。

    (4)部署应用程序。

    该应用程序提供RESTful接口/metrics,并提供名为http_requests_total的自定义指标值。

    (5)创建一个Prometheus的ServiceMonitor对象,用于监控应用程序提供的指标。

    (6)创建一个HorizontalPodAutoscaler对象,用于为HPA控制器提供用户期望的自动扩容配置。

    (7)对应用程序发起HTTP访问请求,验证HPA自动扩容机制。


    参考文章:

    1. https://www.cnblogs.com/cocowool/p/kubernetes_pod_detail.html
    2. https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-strong-getting-started-strong-
    3. https://www.cnblogs.com/linuxk/p/9569618.html
    4. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20
    展开全文
  • pod基础

    2020-12-17 18:39:38
    01-深入Pod原理(共享网络、存储) Pod介绍 Pod存在的意义 Pod的实现机制与设计模式 02-Pod镜像拉取策略 镜像拉取策略 Pod镜像拉取示例 Pod拉取镜像认证方法 03-Pod资源限制 Pod资源限制介绍 Pod资源限制示例 04-Pod...

    01-深入Pod原理(共享网络、存储)

        Pod介绍

        Kubernetes并不直接运行容器,而是使用一个抽象的资源对象来封装一个或多个容器,这个抽象就被称为 Pod,它也是 Kubernetes 的最小调度单元,在Kubernetes中,容器不称为我们之前在Docker中所谓的容器,而是被称为 Pod。同一个 Pod 中可以有多个容器并且同一个Pod中的多个容器共享网络名称和存储资源,这些容器可通过本地回环接口 lo 直接通信,但彼此之间又在 Mount、User、PID等名称空间上保持了隔离。尽管 Pod 中可以包含多个容器,但是作为最小调度单元,它应该尽可能的保持 “小”,所以通常一个Pod中只有一个主容器和其它辅助容器,辅助容器指的是(Filebeats、zabbix_agent客户端等)。

        Pod存在的意义

        Pod主要为亲密性的应用而存在,例如像Nginx+PHP架构,应用+辅助容器,Nginx+Filebeats等类型的容器。

        亲密性应用场景:
        两个应用之间发生文件交互,例如filebeats要读取nginx日志文件进行收集
        两个应用需要通过127.0.0.1或者socket通信,例如nginx+php需要通过lo接口或者socket通信
        两个应用需要发生频繁的调用

        Pod的实现机制与设计模式

        众所周知,容器之间是通过Namespace隔离的,Pod要想解决上述应用场景,那么就要让Pod里的容器之间高效共享,那么Pod之内的容器是如何进行网络共享的呢?

        1.Pod之内的多个容器是怎么进行网络共享的呢?
        kubernetes的解法是这样的:会在每个Pod里先启动一个infra container小容器,然后让其他的容器连接进来这个网络命名空间,然后其他容器看到的网络试图就完全一样了,即网络设备、IP地址、Mac地址等,这就是解决网络共享问题。在Pod的IP地址就是infra container的IP地址。
        k8s会在创建真正的业务容器钱在Pod中创建一个基础容器(infra container)的容器。
        然后让后创建的业务容器连接到基础容器中,一个Pod中的其它所有业务容器共享基础容器的网络、IP地址、Mac地址等。
        基础容器的IP地址就是Pod的IP地址。
        Pod启动的时候,无论Pod中有几个容器,都会创建一个基础容器(infra container),这个基础容器使用的是pause镜像,容器名称也被叫做pause,这个容器非常小,主要取决于它的镜像docker image只有几百kb, pause镜像使用汇编语言编写。
    img
    测试网络
    我们在一个Pod中启动nginx和centos容器,然后在centos中通过lo接口访问nginx的80端口

    cat nginx_network_pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-network-pod
    spec:
      containers:
      - name: nginx-network
        image: nginx:latest
        imagePullPolicy: IfNotPresent
    
      - name: centos-network
        image: centos:latest
        imagePullPolicy: IfNotPresent
        command: [ "/bin/bash", "-ce", "tail -f /dev/null" ]
    

    #创建pod资源对象

    kubectl apply -f manifests/pod/nginx_network_pod.yaml
    

    #查看pod被调度到了哪台Node

    k8sops@k8s-master01:~$ kubectl get pods -o wide | grep nginx-network-pod
    nginx-network-pod                   2/2     Running            0          53s     10.244.3.34   k8s-node01   <none>           <none>
    

    pod被调度到了node1上,我们去node上可以查看由k8s启动的容器,有nginx-network和centos-network容器之外,还有基础容器infra container

    root@k8s-node01:/# docker ps | grep nginx-network
    d3fea735ef5a        470671670cac                                        "/bin/bash -ce 'tail…"   2 minutes ago       Up 2 minutes                            k8s_centos-network_nginx-network-pod_default_c3acfee7-b262-4908-b083-67c5a4e50479_0
    71f0554b5b6a        602e111c06b6                                        "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes                            k8s_nginx-network_nginx-network-pod_default_c3acfee7-b262-4908-b083-67c5a4e50479_0
    fb80158ec9ea        registry.aliyuncs.com/google_containers/pause:3.2   "/pause"                 2 minutes ago       Up 2 minutes                            k8s_POD_nginx-network-pod_default_c3acfee7-b262-4908-b083-67c5a4e50479_0
    

    #基础容器基于pause镜像启动,pause镜像只有683k

    root@k8s-node01:/# docker images | grep pause
     registry.aliyuncs.com/google_containers/pause        3.2                 80d28bedfe5d        2 months ago        683kB
    

    在master节点上进入centos容器,然后通过lo接口访问nginx来进行测试

    kubectl exec -it pods/nginx-network-pod -c centos-network -- /bin/bash
    

    #通过命令可以看到是监听着nginx的网络端口

    [root@nginx-network-pod /]# ss -anplt
    State              Recv-Q              Send-Q                            Local Address:Port                           Peer Address:Port
    LISTEN             0                   128                                     0.0.0.0:80                                  0.0.0.0:*
    

    #但是他们容器之前又隔离PID,所以我们pa aux看到的父进程是我们在yaml语法中定义的命令

    [root@nginx-network-pod /]# ps aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0  23028  1396 ?        Ss   05:34   0:00 /usr/bin/coreutils --coreutils-prog-shebang=tail /usr/bin/tail -f /dev/null
    root         8  0.1  0.0  12028  3264 pts/0    Ss   05:37   0:00 /bin/bash
    root        24  0.0  0.0  43960  3400 pts/0    R+   05:37   0:00 ps aux
    
    #在此容器中通过lo接口访问nginx的服务
    [root@nginx-network-pod /]# curl http://127.0.0.1 -I
    HTTP/1.1 200 OK
    Server: nginx/1.17.10
    Date: Wed, 13 May 2020 05:37:28 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
    Connection: keep-alive
    ETag: "5e95c66e-264"
    Accept-Ranges: bytes
    
    #查看本容器系统版本,确认此容器是centos容器而不是nginx容器
    [root@nginx-network-pod /]# cat /etc/redhat-release
    CentOS Linux release 8.1.1911 (Core)
    
    #确认IP,查看IP是否与Pod中的pause容器进行共享网络
    [root@nginx-network-pod /]# ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    3: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
        link/ether e2:3f:d5:97:57:7d brd ff:ff:ff:ff:ff:ff link-netnsid 0
        inet 10.244.3.34/24 scope global eth0
           valid_lft forever preferred_lft forever   
    
    #退出容器通过Pod IP访问Nginx服务
    [root@nginx-network-pod /]# exit
    exit
    k8sops@k8s-master01:~$ curl http://10.244.3.34 -I
    HTTP/1.1 200 OK
    Server: nginx/1.17.10
    Date: Wed, 13 May 2020 05:40:47 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
    Connection: keep-alive
    ETag: "5e95c66e-264"
    Accept-Ranges: bytes
    

    2.容器之间共享存储
        一个Pod中有两个容器,一个是nginx,另一个是centos容器,那么centos容器就需要读取nginx的日志文件,这个时候就需要让logstash容器读取到nginx容器的日志文件。k8s通过volume将nginx日志文件挂载出来,在本地宿主机生成一个目录,然后centos容器再将挂载出来的日志目录挂载到它自己的容器中,这样就实现了两个容器共享一个文件。

        通过一个yaml配置清单实现一个pod中多容器
        写一个yaml配置清单并观察这两个容器的网络和存储是否像上面描述的一样。
    下面清单中启动了两个容器,分别是nginx和centos容器,nginx容器循环每个一秒往/data/hello文件中写入1-100个数字,写到100个数字即停止,然后由Pod默认的重启策略将Nginx容器重启,重启后再次从1写入到100,以此循环,并且nginx容器挂载了名称为data的卷,挂载到/data目录。
        centos容器也挂载了名称为data的卷,并且挂载到了/data下,同时指定命令动态查看 /data/hello 文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-volume-pod
    spec:
      containers:
      - name: nginx-volume
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        command: [ "/bin/bash", "-ce", "for i in {1..100};do echo $i >> /data/hello;sleep 1;done" ]
        volumeMounts:
        - name: data
          mountPath: /data
    
      - name: centos-volume
        image: centos:latest
        imagePullPolicy: IfNotPresent
        command: [ "/bin/bash", "-ce", "tail -f /data/hello" ]
        volumeMounts:
        - name: data
          mountPath: /data
    
      volumes:
      - name: data
        emptyDir: {}
    
    #name:data:指定了共享卷名称
    #emptyDir: {}:在本地宿主机的路径,如果写为这样,则在相应的node节点上的/var/lib/kubectl/pods下创建相对应的挂载目录
    #创建Pod资源
    kubectl apply -f nginx-volume-pod.yaml
    
    #查看创建的pod被调度到哪台节点
    kubectl get pods -n default -o wide | grep nginx-volume-pod
    nginx-volume-pod                    2/2     Running            0          98s     10.244.3.36   k8s-node01   <none>           <none>
    

    查看centos-volume容器的日志可以看到正在动态查看我们指定的命令
    img
    #进入nginx-volume容器查看 /data/ 目录是否挂载

    k8sops@k8s-master01:~$ kubectl exec -it pods/nginx-volume-pod -c nginx-volume -- /bin/bash
    root@nginx-volume-pod:/# ls -lrth /data/hello
    -rw-r--r-- 1 root root 761 May 13 05:52 /data/hello
    

    #进入centos-volume容器查看 /data/ 目录是否挂载

    k8sops@k8s-master01:~$ kubectl exec -it pods/nginx-volume-pod -c centos-volume -- /bin/bash
    [root@nginx-volume-pod /]# ls /data/hello -lrth
    -rw-r--r-- 1 root root 818 May 13 05:53 /data/hello
    

        我们在宿主机上可以找到容器中挂载的目录及在挂载目录中写入的文件
        在Pod运行的Node主机上,进入 /var/lib/kubectl/pods/目录下,然后使用docker ps 命令查看容器生成的ID,通过ID在当前目录下进入后即可找到挂载的目录,如下图所示:
    img

    02-Pod镜像拉取策略

        官方文档:https://kubernetes.io/docs/concepts/containers/images/

        镜像拉取策略

        Pod镜像拉取策略参数为imagePullPolicy,其值有三个:
        IfNotPresent:默认值,镜像在宿主机上不存在时才拉取
        Always:每次创建 Pod 都会重新拉取一次镜像
        Never: Pod 永远不会主动拉取这个镜像

        Pod镜像拉取示例

        如果要拉取公开镜像,直接使用下面示例即可,但要拉取私有的镜像,是必须认证镜像仓库才可以,文章末尾讲解拉取私人仓库。

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        #imagePullPolicy: Always
        #imagePullPolicy: Never
    

        Pod拉取镜像认证方法

        如果要拉取私人镜像,则需要与镜像仓库进行认证,即docker login,而在K8S集群中会有多个Node,显然这种方式是很不放方便的!为了解决这个问题,K8s实现了自动拉取镜像的功能。 以secret方式保存到K8S中,然后传给kubelet。

    1.生成secret
    在集群的主节点上使用 kubectl create 命令来生成secret

    kubectl create secret docker-registry aliyun-registry --docker-username=useranme --docker-password=password  --docker-server=registry.cn-shanghai.aliyuncs.com
    

    docker-registry:指定生成secret的名称
    –docker-username: 指定docker镜像仓库账号
    –docker-password: 指定docker镜像仓库密码
    –docker-server: 指定docke镜像仓库地址
    –docker-email: 指定邮件地址(选填)

    2.在配置清单中指定secret
    使用参数是 imagePullSecrets,ImagePullSecrets是一个可选的列表,其中包含对同一名称空间中秘密的引用,可用于提取这个PodSpec使用的任何图像。imagePullSecrets下的name参数指定要引用的secrets的名字

    cat aliyun-registry.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: busybox
    spec:
      imagePullSecrets:
      - name: aliyun-registry
      containers:
      - name: busybox
        image: 指定私有镜像仓库地址
        imagePullPolicy: IfNotPresent
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    3.创建配置清单并查看pod

    kubectl apply -f aliyun-registry.yaml
    
    kubectl get pods -o wide | grep busybox
    busybox                             1/1     Running            0          7m59s   10.244.3.44   k8s-node01   <none>           <none>
    

    03-Pod资源限制

        官方文档:https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

    Pod资源限制介绍

    1.Pod资源配额有两种:

    申请配额:

    spec.containers[].resources.requests.cpu
    spec.containers[].resources.requests.memory
    

    限制配额:

    spec.containers[].resources.limits.cpu
    spec.containers[].resources.limits.memory
    

    申请配额是当容器就分配到了这么多资源,限制配额是容器最多能申请这么多资源
    memory单位可以写为: M或者Mi,1M=1000kb,1Mi=1024kb
    cpu单位可以写为:m或者数字,(1000m=1核CPU),(500m=0.5CPU),(250m=0.25CPU)

    Pod资源限制示例

    下面根据官方示例,创建一个pod,pod中两个容器,分别为mysql和wordpress,限制参数请结合上面部分

    apiVersion: v1
    kind: Pod
    metadata:
      name: frontend
    spec:
      containers:
      - name: db
        image: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "password"
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
      - name: wp
        image: wordpress
        resources:
          requests:
            memory: "64M"
            cpu: "0.25"
          limits:
            memory: "128M"
            cpu: "0.5"
    kubectl apply -f limit_pod.yaml
    kubectl get pods -o wide | grep frontend
    frontend                            2/2     Running   2          2m45s   10.244.3.45   k8s-node01   <none>           <none>
    

    kubectl describe pods/frontend
    img

    04-Pod重启策略

    官方文档:https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy

    Pod状态描述

    状态值 描述
    Pending API Server已经创建该Pod,但在Pod内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程。
    Runnung Pod内所有容器均已创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态。
    Succeeded Pod内所有容器均成功执行后退出,且不会再重启。
    Failed Pod内所有容器均已退出,但至少有一个容器退出为失败状态。
    Unknown 由于某种原因无法获取该Pod的状态,可能由于网络通信不畅导致。

    状态列为 STATUS

    k8sops@k8s-master01:~$ kubectl get pods -o wide -n nginx-ns
    NAME               READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
    pod-demo-nginx     1/1     Running   1          14d   10.244.2.15   k8s-node02   <none>           <none>
    pod-demo-nginx02   2/2     Running   15         14d   10.244.2.18   k8s-node02   <none>           <none>
    

    Pod重启策略(RestartPolicy)

        Pod重启策略使用的参数为restartPolicy,字段有三个可选值:

    Always:当容器终止退出后,总是重启容器,默认策略(适用于绝大多数需要24小时不间断运行的应用)
    OnFailure:当容器异常退出(退出状态码非0)时,才重启容器。适于job(适用于一次性计划任务或者批处理任务,当进行计划任务或者批处理的跑失败的时候,在linux中会异常退出,这个时候才会重启容器,然后重新跑计划任务,如果计划任务成功,则返回0,正常退出,正常退出后则不在启动容器,符合预期,linux中正常运行返回0,异常退出返回非0,)
    Never:当容器终止退出,从不重启容器。适于job(让无论是正常退出还是异常退出都不重启容器,一般用的比较少)

    Pod Always重启策略示例

    1.编写配置清单

    cat restart_pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: restart-pod
    spec:
      containers:
      - name: restart-containers
        image: nginx:latest
      restartPolicy: Always
    

    2.创建一个Pod资源

    kubectl apply -f restart_pod.yaml
    

    3.找到pod所运行的Node

    kubectl get pods -o wide | grep restart-pod
    restart-pod                         1/1     Running            1          5m58s   10.244.5.34   k8s-node03   <none>           <none>
    

    4.到相应的Node上给结束掉这个Pod,不能在master上使用 kubectl delete pods/restart-pod 来介绍,因为这样会删掉Pod

    #找到相应的容器

    root@k8s-node03:~# docker ps | grep restart
    32c70e9b113e        nginx                                               "nginx -g 'daemon of…"   10 minutes ago      Up 10 minutes                           k8s_restart-containers_restart-pod_default_c6ba7906-d5a7-47f8-b523-2a4ecddbc552_1
    ea7e9d98da19        registry.aliyuncs.com/google_containers/pause:3.2   "/pause"                 13 minutes ago      Up 13 minutes                           k8s_POD_restart-pod_default_c6ba7906-d5a7-47f8-b523-2a4ecddbc552_0
    

    #根据容器ID Stop掉容器

    root@k8s-node03:~# docker stop 32c70e9b113e
    32c70e9b113e
    

    5.然后到master上会发现以下过程

    #Pod已完成工作

    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep restart-pod
    restart-pod                         0/1     Completed          2          14m     10.244.5.34   k8s-node03   <none>           <none>
    

    #Pod等待中

    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep restart-pod
    restart-pod                         0/1     CrashLoopBackOff   2          15m     10.244.5.34   k8s-node03   <none>           <none>
    

    #Pod已经正常再次运行起来

    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep restart-pod
    restart-pod                         1/1     Running            3          15m     10.244.5.34   k8s-node03   <none>           <none>
    

    6.通过 kubectl describe 命令查看更详细的事件信息
    img

    Pod OnFailure重启策略示例

    1.编写资源配置清单
    以下运行了一个Pod,容器镜像为centos,在容器中运行一个脚本,在/data/hello文件中写入数字1-300,每隔一秒写一个数字,写完后脚本执行结束,则退出容器(需要300秒,5分钟),重启策略使用 OnFailure,我们这属于正常退出,退出后则不会再次启动容器。

    cat restart_pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: restart-pod
    spec:
      containers:
      - name: restart-containers
        image: centos:centos7.6.1810
        command:  [ "/bin/bash", "-ce", "for i in {1..300};do echo $i >> /hello;sleep 1;done" ]
      restartPolicy: OnFailure
    

    2.创建资源对象

    kubectl apply -f restart_pod.yaml
    

    3.查看Pod状态

    kubectl get pods -o wide | grep restart-pod
    restart-pod                         1/1     Running            0          11s     10.244.2.28   k8s-node02   <none>           <none>
    

    4.进入容器

    kubectl exec -it pods/restart-pod -- /bin/bash
    

    5.查看容器执行脚本进程
    img
    6.容器停止
    当等待到5分钟,容器将脚本执行完成后,则退出容器,Pod状态也变为了 “Completed完成” 状态
    img
    因为我们使用的重启策略是OnFailure,我们的容器也属于正常退出,所以不会再去自动启动此容器

    7.测试异常退出
    再次创建此Pod,然后到指定的Node上停止此容器

    kubectl apply -f restart_pod.yaml
    

    #restart-pod容器被调度到了node2上

    kubectl get pods -o wide | grep restart-pod
    restart-pod                         1/1     Running            0          26s     10.244.2.29   k8s-node02   <none>           <none>
    

    #来到node2上手动停止此容器

    root@k8s-node02:~# docker ps | grep restart
    5943923ce8ab        f1cb7c7d58b7                                        "/bin/bash -ce 'for …"   56 seconds ago      Up 55 seconds                           k8s_restart-containers_restart-pod_default_ae58b877-36b7-49da-b984-1d7f2a9e42da_0
    1fc5c7dcc18d        registry.aliyuncs.com/google_containers/pause:3.2   "/pause"                 58 seconds ago      Up 56 seconds                           k8s_POD_restart-pod_default_ae58b877-36b7-49da-b984-1d7f2a9e42da_0
    root@k8s-node02:~# docker stop 5943923ce8ab
    5943923ce8ab
    

    #再回到master上观察restart-pod状态,可以看到下面第一次看pod状态,已经为错误状态,紧接着再看一次Pod已经被重新启动起来,这就是被异常退出后通过OnFailure策略拉起来的Pod

    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep restart-pod
    restart-pod                         0/1     Error              0          81s     10.244.2.29   k8s-node02   <none>           <none>
    
    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep restart-pod
    restart-pod                         1/1     Running            1          84s     10.244.2.29   k8s-node02   <none>           <none>
    

    05-Pod健康检查

    Pod健康检查介绍

    默认情况下,kubelet根据容器运行状态作为健康依据,不能监控容器中应用程序状态,例如程序假死。这就会导致无法提供服务,丢失流量。因此引入健康检查机制确保容器健康存活。
    Pod通过两类探针来检查容器的健康状态。分别是LivenessProbe(存活探测)和 ReadinessProbe(就绪探测)。

    livenessProbe(存活探测)

    存活探测将通过http、shell命令或者tcp等方式去检测容器中的应用是否健康,然后将检查结果返回给kubelet,如果检查容器中应用为不健康状态提交给kubelet后,kubelet将根据Pod配置清单中定义的重启策略restartPolicy来对Pod进行重启。

    readinessProbe(就绪探测)

    就绪探测也是通过http、shell命令或者tcp等方式去检测容器中的应用是否健康或则是否能够正常对外提供服务,如果能够正常对外提供服务,则认为该容器为(Ready状态),达到(Ready状态)的Pod才可以接收请求。

    对于被Service所管理的Pod,Service与被管理Pod的关联关系也将基于Pod是否Ready进行设置,Pod对象启动后,容器应用通常需要一段时间才能完成其初始化的过程,例如加载配置或数据,甚至有些程序需要运行某类的预热过程,若在此阶段完成之前就接收客户端的请求,那么客户端返回时间肯定非常慢,严重影响了体验,所以因为避免Pod对象启动后立即让其处理客户端请求,而是等待容器初始化工作执行完成并转为Ready状态后再接收客户端请求。

    如果容器或则Pod状态为(NoReady)状态,Kubernetes则会把该Pod从Service的后端endpoints Pod中去剔除。

    健康检测实现方式

    以上介绍了两种探测类型livenessProbe(存活探测),readinessProbe(就绪探测),这两种探测都支持以下方式对容器进行健康检查

    ExecAction:在容器中执行命令,命令执行后返回的状态为0则成功,表示我们探测结果正常
    HTTPGetAction:根据容器IP、端口以及路径发送HTTP请求,返回码如果是200-400之间表示成功
    TCPSocketAction:根据容器IP地址及特定的端口进行TCP检查,端口开放表示成功
    以上每种检查动作都可能有以下三种返回状态

    Success,表示通过了健康检查
    Failure,表示没有通过健康检查
    Unknown,表示检查动作失败

    livenessProbe存活探测示例

    livenessProbe for ExecActiion 示例

    通过在目标容器中执行由用户自定义的命令来判定容器的健康状态,即在容器内部执行一个命令,如果改命令的返回码为0,则表明容器健康。spec.containers.LivenessProbe字段用于定义此类检测,它只有一个可用属性command,用于指定要执行的命令,下面是在资源清单文件中使用liveness-exec方式的示例:

    1.创建资源配置清单
    创建一个Pod——》运行Nginx容器——》首先启动nginx——》然后沉睡60秒后——〉删除nginx.pid
    通过livenessProbe存活探测的exec命令判断nginx.pid文件是否存在,如果探测返回结果非0,则按照重启策略进行重启。
    预期是容器真正(Ready)状态60s后,删除nginx.pid,exec命令探测生效,按照重启策略进行重启

    cat ngx-health.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: ngx-health
    spec:
      containers:
      - name: ngx-liveness
        image: nginx:latest
        command:
        - /bin/sh
        - -c
        - /usr/sbin/nginx; sleep 60; rm -rf /run/nginx.pid
        livenessProbe:
          exec:
            command: [ "/bin/sh", "-c", "test", "-e", "/run/nginx.pid" ]
      restartPolicy: Always
    

    2.创建Pod资源

    kubectl apply -f ngx-health.yaml
    

    等待Pod Ready

    3.查看Pod的详细信息

    #第一次查看,Pod中的容器启动成功,事件正常

    kubectl describe pods/ngx-health | grep -A 10 Events
    Events:
      Type    Reason     Age        From                 Message
      ----    ------     ----       ----                 -------
      Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/ngx-health to k8s-node03
      Normal  Pulling    12s        kubelet, k8s-node03  Pulling image "nginx:latest"
      Normal  Pulled     6s         kubelet, k8s-node03  Successfully pulled image "nginx:latest"
      Normal  Created    6s         kubelet, k8s-node03  Created container ngx-liveness
      Normal  Started    5s         kubelet, k8s-node03  Started container ngx-liveness
      
    

    #第二次查看,容器的livenessProbe探测失败,

    kubectl describe pods/ngx-health | grep -A 10 Events
    Events:
      Type     Reason     Age                From                 Message
      ----     ------     ----               ----                 -------
      Normal   Scheduled  <unknown>          default-scheduler    Successfully assigned default/ngx-health to k8s-node03
      Normal   Pulling    52s                kubelet, k8s-node03  Pulling image "nginx:latest"
      Normal   Pulled     46s                kubelet, k8s-node03  Successfully pulled image "nginx:latest"
      Normal   Created    46s                kubelet, k8s-node03  Created container ngx-liveness
      Normal   Started    45s                kubelet, k8s-node03  Started container ngx-liveness
      Warning  Unhealthy  20s (x3 over 40s)  kubelet, k8s-node03  Liveness probe failed:
      Normal   Killing    20s                kubelet, k8s-node03  Container ngx-liveness failed liveness probe, will be restarted
    

    #第三次查看,已经重新拉取镜像,然后创建容器再启动容器

    kubectl describe pods/ngx-health | grep -A 10 Events
    Events:
      Type     Reason     Age                From                 Message
      ----     ------     ----               ----                 -------
      Normal   Scheduled  <unknown>          default-scheduler    Successfully assigned default/ngx-health to k8s-node03
      Warning  Unhealthy  35s (x3 over 55s)  kubelet, k8s-node03  Liveness probe failed:
      Normal   Killing    35s                kubelet, k8s-node03  Container ngx-liveness failed liveness probe, will be restarted
      Normal   Pulling    4s (x2 over 67s)   kubelet, k8s-node03  Pulling image "nginx:latest"
      Normal   Pulled     2s (x2 over 61s)   kubelet, k8s-node03  Successfully pulled image "nginx:latest"
      Normal   Created    2s (x2 over 61s)   kubelet, k8s-node03  Created container ngx-liveness
      Normal   Started    2s (x2 over 60s)   kubelet, k8s-node03  Started container ngx-liveness
    

    通过长格式输出可以看到如下,第一次长格式输出Pod运行时间22s,重启次数为0
    第二次长格式输出,运行时间是76s,Pod已经完成一次重启

    kubectl get pods -o wide | grep ngx-health
    ngx-health                          1/1     Running            0          22s     10.244.5.44   k8s-node03   <none>           <none>
    
    kubectl get pods -o wide | grep ngx-health
    ngx-health                          1/1     Running            1          76s     10.244.5.44   k8s-node03   <none>           <none>
    

    第二次健康探测失败及第二次重启

    kubectl describe pods/ngx-health | grep -A 10 Events
    Events:
      Type     Reason     Age                 From                 Message
      ----     ------     ----                ----                 -------
      Normal   Scheduled  <unknown>           default-scheduler    Successfully assigned default/ngx-health to k8s-node03
      Normal   Pulled     58s (x2 over 117s)  kubelet, k8s-node03  Successfully pulled image "nginx:latest"
      Normal   Created    58s (x2 over 117s)  kubelet, k8s-node03  Created container ngx-liveness
      Normal   Started    58s (x2 over 116s)  kubelet, k8s-node03  Started container ngx-liveness
      Warning  Unhealthy  31s (x6 over 111s)  kubelet, k8s-node03  Liveness probe failed:
      Normal   Killing    31s (x2 over 91s)   kubelet, k8s-node03  Container ngx-liveness failed liveness probe, will be restarted
      Normal   Pulling    0s (x3 over 2m3s)   kubelet, k8s-node03  Pulling image "nginx:latest"
      
    kubectl get pods -o wide | grep ngx-health
    ngx-health                          1/1     Running            2          2m13s   10.244.5.44   k8s-node03   <none>           <none>
    

    livenessProbe for HTTPGetAction示例

    通过容器的ip地址,端口号及路径调用HTTPGet方法,如果响应的状态码大于等于200且小于400,则认为容器健康,spec.containers.livenessProbe.httpGet字段用于定义此类检测,它的可用配置字段包括如下几个:

    host :请求的主机地址,默认为Pod IP;也可以在httpHeaders中使用 Host: 来定义
    port :请求的端口,必选字段,端口范围1-65535
    httpHeaders <[]Object>:自定义的请求报文首部
    path :请求的HTTP资源路径,即URL path
    scheme:建立连接使用的协议,仅可为HTTP或HTTPS,默认为HTTP
    1.创建资源配置清单
    创建一个Pod——》运行Nginx容器——》首先启动nginx——》然后沉睡60秒后——〉删除nginx.pid
    通过livenessProbe存活探测的httpGet方式请求nginx项目根目录下的index.html文件,访问端口为80,访问地址默认为Pod IP,请求协议为HTTP,如果请求失败则按照重启策略进行重启。

    cat ngx-health.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: ngx-health
    spec:
      containers:
      - name: ngx-liveness
        image: nginx:latest
        command:
        - /bin/sh
        - -c
        - /usr/sbin/nginx; sleep 60; rm -rf /run/nginx.pid
        livenessProbe:
          httpGet:
            path: /index.html
            port: 80
            scheme: HTTP
      restartPolicy: Always
    

    2.创建Pod资源对象

    kubectl apply -f ngx-health.yaml
    

    3.查看Pod运行状态

    #容器创建

    kubectl get pods -o wide | grep ngx-health
    ngx-health                          0/1     ContainerCreating   0          7s      <none>        k8s-node02   <none>           <none>
    

    #容器运行成功

    kubectl get pods -o wide | grep ngx-health
    ngx-health                          1/1     Running            0          19s     10.244.2.36   k8s-node02   <none>           <none>
    

    4.查看Pod的详细事件信息
    容器镜像拉取并启动成功

    kubectl describe pods/ngx-health | grep -A 10 Events
    Events:
      Type    Reason     Age        From                 Message
      ----    ------     ----       ----                 -------
      Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/ngx-health to k8s-node02
      Normal  Pulling    30s        kubelet, k8s-node02  Pulling image "nginx:latest"
      Normal  Pulled     15s        kubelet, k8s-node02  Successfully pulled image "nginx:latest"
      Normal  Created    15s        kubelet, k8s-node02  Created container ngx-liveness
      Normal  Started    14s        kubelet, k8s-node02  Started container ngx-liveness
    

    容器ready状态后运行60s左右livenessProbe健康检测,可以看到下面已经又开始拉取镜像

    kubectl describe pods/ngx-health | grep -A 15 Events
    Events:
      Type    Reason     Age               From                 Message
      ----    ------     ----              ----                 -------
      Normal  Scheduled  <unknown>         default-scheduler    Successfully assigned default/ngx-health to k8s-node02
      Normal  Pulled     63s               kubelet, k8s-node02  Successfully pulled image "nginx:latest"
      Normal  Created    63s               kubelet, k8s-node02  Created container ngx-liveness
      Normal  Started    62s               kubelet, k8s-node02  Started container ngx-liveness
      Normal  Pulling    1s (x2 over 78s)  kubelet, k8s-node02  Pulling image "nginx:latest"
    

    镜像拉取完后再次重启创建并启动了一遍,可以看到 Age 列的时间已经重新计算

    kubectl describe pods/ngx-health | grep -A 15 Events
    Events:
      Type    Reason     Age                From                 Message
      ----    ------     ----               ----                 -------
      Normal  Scheduled  <unknown>          default-scheduler    Successfully assigned default/ngx-health to k8s-node02
      Normal  Pulling    18s (x2 over 95s)  kubelet, k8s-node02  Pulling image "nginx:latest"
      Normal  Pulled     2s (x2 over 80s)   kubelet, k8s-node02  Successfully pulled image "nginx:latest"
      Normal  Created    2s (x2 over 80s)   kubelet, k8s-node02  Created container ngx-liveness
      Normal  Started    1s (x2 over 79s)   kubelet, k8s-node02  Started container ngx-liveness
    

    长格式输出Pod,可以看到Pod已经重启过一次

    kubectl get pods -o wide | grep ngx-health
    ngx-health                          0/1     Completed          0          96s     10.244.2.36   k8s-node02   <none>           <none>
    k8sops@k8s-master01:~/manifests/pod$ kubectl get pods -o wide | grep ngx-health
    ngx-health                          1/1     Running            1          104s    10.244.2.36   k8s-node02   <none>           <none>
    

    通过查看容器日志,可以看到下面的探测日志,默认10秒探测一次

    kubectl logs -f pods/ngx-health
    10.244.2.1 - - [15/May/2020:03:01:13 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    10.244.2.1 - - [15/May/2020:03:01:23 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    10.244.2.1 - - [15/May/2020:03:01:33 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    10.244.2.1 - - [15/May/2020:03:01:43 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    10.244.2.1 - - [15/May/2020:03:01:53 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    10.244.2.1 - - [15/May/2020:03:02:03 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.18" "-"
    

    livenessProbe for TCPSocketAction示例

    通过容器的IP地址和端口号进行TCP检查,如果能够建立TCP连接,则表明容器健康。相比较来说,它比基于HTTP的探测要更高效,更节约资源,但精准度略低,毕竟建立连接成功未必意味着页面资源可用,spec.containers.livenessProbe.tcpSocket字段用于定义此类检测,它主要包含以下两个可用的属性:

    host:请求连接的目标IP地址,默认为Pod IP
    port:请求连接的目标端口,必选字段
    下面是在资源清单文件中使用liveness-tcp方式的示例,它向Pod IP的80/tcp端口发起连接请求,并根据连接建立的状态判定测试结果:
    1.创建资源配置清单

    apiVersion: v1
    kind: Pod
    metadata:
      name: ngx-health
    spec:
      containers:
      - name: ngx-liveness
        image: nginx:latest
        command:
        - /bin/sh
        - -c
        - /usr/sbin/nginx; sleep 60; rm -rf /run/nginx.pid
        livenessProbe:
          tcpSocket:
            port: 80
      restartPolicy: Always
    

    2.创建资源对象

    kubectl apply -f ngx-health.yaml
    

    3.查看Pod创建属性信息

    #容器创建并启动成功

    kubectl describe pods/ngx-health | grep -A 15 Events
    Events:
      Type    Reason     Age        From                 Message
      ----    ------     ----       ----                 -------
      Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/ngx-health to k8s-node02
      Normal  Pulling    19s        kubelet, k8s-node02  Pulling image "nginx:latest"
      Normal  Pulled     9s         kubelet, k8s-node02  Successfully pulled image "nginx:latest"
      Normal  Created    8s         kubelet, k8s-node02  Created container ngx-liveness
      Normal  Started    8s         kubelet, k8s-node02  Started container ngx-liveness
    

    #在容器ready状态后60s左右Pod已经有了再次拉取镜像的动作

    kubectl describe pods/ngx-health | grep -A 15 Events
    Events:
      Type    Reason     Age                From                 Message
      ----    ------     ----               ----                 -------
      Normal  Scheduled  <unknown>          default-scheduler    Successfully assigned default/ngx-health to k8s-node02
      Normal  Pulled     72s                kubelet, k8s-node02  Successfully pulled image "nginx:latest"
      Normal  Created    71s                kubelet, k8s-node02  Created container ngx-liveness
      Normal  Started    71s                kubelet, k8s-node02  Started container ngx-liveness
      Normal  Pulling    10s (x2 over 82s)  kubelet, k8s-node02  Pulling image "nginx:latest"
    

    #通过长格式输出Pod,也可以看到当前Pod已经进入了完成的状态,接下来就是重启Pod

     kubectl get pods -o wide | grep ngx-health
    ngx-health                          0/1     Completed          0          90s     10.244.2.37   k8s-node02   <none>           <none>
    

    健康检测参数

        上面介绍了两种在不同时间段的探测方式,以及两种探测方式所支持的探测方法,这里介绍几个辅助参数

    initialDelaySeconds:检查开始执行的时间,以容器启动完成为起点计算
    periodSeconds:检查执行的周期,默认为10秒,最小为1秒
    successThreshold:从上次检查失败后重新认定检查成功的检查次数阈值(必须是连续成功),默认为1,也必须是1
    timeoutSeconds:检查超时的时间,默认为1秒,最小为1秒
    failureThreshold:从上次检查成功后认定检查失败的检查次数阈值(必须是连续失败),默认为1

    健康检测实践

    以下示例使用了就绪探测readinessProbe和存活探测livenessProbe
    就绪探测配置解析:

    容器在启动5秒initialDelaySeconds后进行第一次就绪探测,将通过http访问探测容器网站根目录下的index.html文件,如果探测成功,则Pod将被标记为(Ready)状态。
    然后就绪检测通过periodSeconds参数所指定的间隔时间进行循环探测,下面我所指定的间隔时间是10秒钟,每隔10秒钟就绪探测一次。
    每次探测超时时间为3秒,如果探测失败1次就将此Pod从Service的后端Pod中剔除,剔除后客户端请求将无法通过Service访问到其Pod。
    就绪探测还会继续对其进行探测,那么如果发现此Pod探测成功1次,通过successThreshold参数设定的值,那么会将它再次加入后端Pod。
    存活探测配置解析

    容器在启动15秒initialDelaySeconds后进行第一次存活探测,将通过tcpSocket探测容器的80端口,如果探测返回值为0则成功。
    每次存活探测间隔为3秒钟,每次探测超时时间为1秒,如果连续探测失败2次则通过重启策略重启Pod。
    检测失败后的Pod,存活探测还会对其进行探测,如果再探测成功一次,那么将认为此Pod为健康状态
    1.资源配置清单

    cat nginx-health.yaml
    #create namespace
    apiVersion: v1
    kind: Namespace
    metadata:
      name: nginx-health-ns
      labels:
        resource: nginx-ns
    spec:
    
    ---
    
    #create deploy and pod
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx-health-deploy
      namespace: nginx-health-ns
      labels:
        resource: nginx-deploy
    spec:
      replicas: 3
      revisionHistoryLimit: 10
      selector:
        matchLabels:
          app: nginx-health
      template:
        metadata:
          namespace: nginx-health-ns
          labels:
            app: nginx-health
        spec:
          restartPolicy: Always
          containers:
          - name: nginx-health-containers
            image: nginx:1.17.1
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - /usr/sbin/nginx; sleep 60; rm -rf /run/nginx.pid
            readinessProbe:
              initialDelaySeconds: 5
              periodSeconds: 10
              successThreshold: 1
              timeoutSeconds: 3
              failureThreshold: 1
              httpGet:
                path: /index.html
                port: 80
                scheme: HTTP
            livenessProbe:
              initialDelaySeconds: 15
              periodSeconds: 3
              successThreshold: 1
              timeoutSeconds: 1
              failureThreshold: 2
              tcpSocket:
                port: 80
            resources:
              requests:
                memory: "64Mi"
                cpu: "250m"
              limits:
                memory: "128Mi"
                cpu: "500m"
    
    #create service
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-health-svc
      namespace: nginx-health-ns
      labels:
        resource: nginx-svc
    spec:
       clusterIP: 10.106.189.88
       ports:
       - port: 80
         protocol: TCP
         targetPort: 80
       selector:
         app: nginx-health
       sessionAffinity: ClientIP
       type: ClusterIP
    

    2.创建资源对象

    kubectl apply -f nginx-health.yaml
    namespace/nginx-health-ns created
    deployment.apps/nginx-health-deploy created
    service/nginx-health-svc created
    

    3.查看创建的资源对象

    k8sops@k8s-master01:/$ kubectl get all -n nginx-health-ns -o wide
    NAME                                       READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
    pod/nginx-health-deploy-6bcc8f7f74-6wc6t   1/1     Running   0          24s   10.244.3.50   k8s-node01   <none>           <none>
    pod/nginx-health-deploy-6bcc8f7f74-cns27   1/1     Running   0          24s   10.244.5.52   k8s-node03   <none>           <none>
    pod/nginx-health-deploy-6bcc8f7f74-rsxjj   1/1     Running   0          24s   10.244.2.42   k8s-node02   <none>           <none>
    
    NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE   SELECTOR
    service/nginx-health-svc   ClusterIP   10.106.189.88   <none>        80/TCP    25s   app=nginx-health
    
    NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS                IMAGES         SELECTOR
    deployment.apps/nginx-health-deploy   3/3     3            3           25s   nginx-health-containers   nginx:1.17.1   app=nginx-health
    
    NAME                                             DESIRED   CURRENT   READY   AGE   CONTAINERS                IMAGES         SELECTOR
    replicaset.apps/nginx-health-deploy-6bcc8f7f74   3         3         3       25s   nginx-health-containers   nginx:1.17.1   app=nginx-health,pod-template-hash=6bcc8f7f74
    

    4.查看Pod状态,目前Pod状态都没有就绪并且完成状态,准备重启

    k8sops@k8s-master01:/$ kubectl get pods -n nginx-health-ns -o wide
    NAME                                   READY   STATUS      RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
    nginx-health-deploy-6bcc8f7f74-6wc6t   0/1     Completed   0          64s   10.244.3.50   k8s-node01   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-cns27   0/1     Completed   0          64s   10.244.5.52   k8s-node03   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-rsxjj   0/1     Completed   0          64s   10.244.2.42   k8s-node02   <none>           <none>
    

    5.目前已经有一台Pod完成重启,已准备就绪

    kubectl get pods -n nginx-health-ns -o wide
    NAME                                   READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
    nginx-health-deploy-6bcc8f7f74-6wc6t   1/1     Running   1          73s   10.244.3.50   k8s-node01   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-cns27   0/1     Running   1          73s   10.244.5.52   k8s-node03   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-rsxjj   0/1     Running   1          73s   10.244.2.42   k8s-node02   <none>           <none>
    

    6.三台Pod都均完成重启,已准备就绪

    kubectl get pods -n nginx-health-ns -o wide
    NAME                                   READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
    nginx-health-deploy-6bcc8f7f74-6wc6t   1/1     Running   1          85s   10.244.3.50   k8s-node01   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-cns27   1/1     Running   1          85s   10.244.5.52   k8s-node03   <none>           <none>
    nginx-health-deploy-6bcc8f7f74-rsxjj   1/1     Running   1          85s   10.244.2.42   k8s-node02   <none>           <none>
    

    7.在Pod重启的时候,可以看到Service可以动态关联和取消相关的Pod
    img

    06-Pod调度策略

    调度概览

    调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod。调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行。调度器会依据调度原则来做出调度选择。

    本章节要记录的调度策略有以下几种:

        kube-scheduler
        nodeName
        nodeSelector
        污点与容忍度
        Node亲和性调度
        Pod亲和性调度
        Pod互斥性调度

    Pod工作流程

        创建一个Pod的工作流程
    管理员通过配置清单然后使用kubectl命令创建一个Pod资源对象,kubectl会将POST请求提交给API Server,在API Server收到POST请求前,k8s系统会将yaml配置清单转换为JSON格式提交给API Server。create pod阶段。
    API Server收到kubectl提交的POST请求后,将kubectl提交的内容写入到etcd中进行存储。write etcd阶段。
    Scheduler组件一直在不间断的watch API Server,Seheduler发现有新的资源对象需要创建,那么Scheduler将通过自身的算法把资源对象调度到相应的Node上,bind pod阶段。
    然后API Server将Scheduler调度的事件写入到etcd中。write etcd阶段。
    Kubelet组件也在一直watch API Server状态,Kubelet从API Server得知新的资源是调度到本机来的,然后kubelet调用本机的docker进行创建容器。
    docker创建容器后,kubelet将状态汇报给API Server。
    API Server收到kubelet汇报的状态后将信息写入etcd
    img

    kube-scheduler调度流程

        官方文档:https://kubernetes.io/zh/docs/concepts/scheduling-eviction/kube-scheduler/

    kube-scheduler调度介绍
        kube-scheduler 是 Kubernetes 集群的默认调度器,并且是集群控制面(master)的一部分。
        对每一个新创建的 Pod 或者是未被调度的 Pod,kube-scheduler 会选择一个最优的 Node 去运行这个 Pod。然而,Pod 内的每一个容器对资源都有不同的需求,而且 Pod 本身也有不同的资源需求。因此,Pod 在被调度到 Node 上之前,根据这些特定的资源调度需求,需要对集群中的 Node 进行一次过滤。

    在一个集群中,满足一个 Pod 调度请求的所有 Node 称之为 可调度节点。如果没有任何一个 Node 能满足 Pod 的资源请求,那么这个 Pod 将一直停留在未调度状态直到调度器能够找到合适的 Node。

    调度器先在集群中找到一个 Pod 的所有可调度节点,然后根据一系列函数对这些可调度节点打分,然后选出其中得分最高的 Node 来运行 Pod。之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做 绑定。

    在做调度决定时需要考虑的因素包括:单独和整体的资源请求、硬件/软件/策略限制、亲和以及反亲和要求、数据局域性、负载间的干扰等等。

    kube-scheduler 调度流程

    kube-scheduler 给一个 pod 做调度选择包含两个步骤:
        过滤(Predicates预选策略)
        打分(Priorities优选策略)
    **过滤阶段:**过滤阶段会将所有满足 Pod 调度需求的 Node 选出来。例如,PodFitsResources 过滤函数会检查候选 Node 的可用资源能否满足 Pod 的资源请求。在过滤之后,得出一个 Node 列表,里面包含了所有可调度节点;通常情况下,这个 Node 列表包含不止一个 Node。如果这个列表是空的,代表这个 Pod 不可调度。

    **打分阶段:**在过滤阶段后调度器会为 Pod 从所有可调度节点中选取一个最合适的 Node。根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。最后,kube-scheduler 会将 Pod 调度到得分最高的 Node 上。如果存在多个得分最高的 Node,kube-scheduler 会从中随机选取一个。

    偷一张图
    img

        官方文档:https://kubernetes.io/docs/reference/scheduling/policies/

    过滤阶段需求:

    PodFitsHostPorts:检查Node上是否不存在当前被调度Pod的端口(如果被调度Pod用的端口已被占用,则此Node被Pass)。
    PodFitsHost:检查Pod是否通过主机名指定了特性的Node (是否在Pod中定义了nodeName)。
    PodFitsResources:检查Node是否有空闲资源(如CPU和内存)以满足Pod的需求。
    PodMatchNodeSelector:检查Pod是否通过节点选择器选择了特定的Node (是否在Pod中定义了nodeSelector)。
    NoVolumeZoneConflict:检查Pod请求的卷在Node上是否可用 (不可用的Node被Pass)。
    NoDiskConflict:根据Pod请求的卷和已挂载的卷,检查Pod是否合适于某个Node (例如Pod要挂载/data到容器中,Node上/data/已经被其它Pod挂载,那么此Pod则不适合此Node)
    MaxCSIVolumeCount::决定应该附加多少CSI卷,以及是否超过了配置的限制。
    CheckNodeMemoryPressure:对于内存有压力的Node,则不会被调度Pod。
    CheckNodePIDPressure:对于进程ID不足的Node,则不会调度Pod。
    CheckNodeDiskPressure:对于磁盘存储已满或者接近满的Node,则不会调度Pod。
    CheckNodeCondition:Node报告给API Server说自己文件系统不足,网络有写问题或者kubelet还没有准备好运行Pods等问题,则不会调度Pod。
    PodToleratesNodeTaints:检查Pod的容忍度是否能承受被打上污点的Node。
    CheckVolumeBinding:根据一个Pod并发流量来评估它是否合适(这适用于结合型和非结合型PVCs)。
    打分阶段需求:

    SelectorSpreadPriority:优先减少节点上属于同一个 Service 或 Replication Controller 的 Pod 数量
    InterPodAffinityPriority:优先将 Pod 调度到相同的拓扑上(如同一个节点、Rack、Zone 等)
    LeastRequestedPriority:节点上放置的Pod越多,这些Pod使用的资源越多,这个Node给出的打分就越低,所以优先调度到Pod少及资源使用少的节点上。
    MostRequestedPriority:尽量调度到已经使用过的 Node 上,将把计划的Pods放到运行整个工作负载所需的最小节点数量上。
    RequestedToCapacityRatioPriority:使用默认资源评分函数形状创建基于requestedToCapacity的ResourceAllocationPriority。
    BalancedResourceAllocation:优先平衡各节点的资源使用。
    NodePreferAvoidPodsPriority:根据节点注释对节点进行优先级排序,以使用它来提示两个不同的 Pod 不应在同一节点上运行。scheduler.alpha.kubernetes.io/preferAvoidPods。
    NodeAffinityPriority:优先调度到匹配 NodeAffinity (Node亲和性调度)的节点上。
    TaintTolerationPriority:优先调度到匹配 TaintToleration (污点) 的节点上
    ImageLocalityPriority:尽量将使用大镜像的容器调度到已经下拉了该镜像的节点上。
    ServiceSpreadingPriority:尽量将同一个 service 的 Pod 分布到不同节点上,服务对单个节点故障更具弹性。
    EqualPriority:将所有节点的权重设置为 1。
    EvenPodsSpreadPriority:实现首选pod拓扑扩展约束。

    kube-scheduler 调度示例

    默认配置使用的就是kube-scheduler调度组件,我们下面例子启动三个Pod,看分别被分配到哪个Node。
    1.创建资源配置清单

    cat scheduler-pod.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: scheduler-deploy
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: scheduler-pod
      template:
        metadata:
          labels:
            app: scheduler-pod
        spec:
          containers:
          - image: busybox:latest
            name: scheduler-pod
            command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    2.使用kubectl创建资源对象

    kubectl apply -f scheduler-pod.yaml
    

    3.查看被kube-scheduler自动调度的Pod
    两个Pod在Node03上,一个在Node02上

    kubectl get pods -o wide | grep scheduler
    scheduler-deploy-65d8f9c98-cqdm9    1/1     Running            0          111s    10.244.5.59   k8s-node03   <none>           <none>
    scheduler-deploy-65d8f9c98-d4t9p    1/1     Running            0          111s    10.244.5.58   k8s-node03   <none>           <none>
    scheduler-deploy-65d8f9c98-f8xxc    1/1     Running            0          111s    10.244.2.45   k8s-node02   <none>           <none>
    

    4.我们查看一下Node资源的使用情况
    Node01,可用内存2.7G
    img

    Node02,可用内存5.8G
    img

    Node03,可用内存5.6G
    img

    nodeName

    nodeName会将Pod调度到指定的Node上

    1.创建资源配置清单

    cat nodeName-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nodename-pod
    spec:
      nodeName: k8s-node02
      containers:
      - image: busybox:latest
        name: nodename-containers
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    2.创建Pod资源对象
    如下,nodename-pod被绑定在了k8s-node02上

    kubectl get pods -o wide | grep name
    nodename-pod                        1/1     Running            0          25s     10.244.2.46   k8s-node02   <none>           <none>
    

    nodeSelector

    nodeSelector用于将Pod调度到匹配Label的Node上,所以要先给node打上标签,然后在Pod配置清单中选择指定Node的标签。
    先给规划node用途,然后打标签,例如将两台node划分给不同团队使用:

    1.为Node添加标签
        node02给开发团队用,node03给大数据团队用

    #添加标签

    kubectl label nodes k8s-node02 team=development
    kubectl label nodes k8s-node03 team=bigdata
    

    #查看标签

    kubectl get nodes -o wide --show-labels
    

    2.创建资源配置清单

    cat nodeSelector-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: nodeselector-pod
    spec:
      nodeSelector:
        team: development
      containers:
      - image: busybox:latest
        name: nodeselector-containers
        command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    3.创建Pod资源对象

    kubectl apply -f nodeSelector-pod.yaml
    

    4.查看pod被分配的Node

    kubectl get pods -o wide | grep nodeselect
    nodeselector-pod                    1/1     Running            0          49s     10.244.2.47   k8s-node02   <none>           <none>
    

    4.删除标签

    kubectl label nodes k8s-node02 team-
    kubectl label nodes k8s-node03 team-
    

    删除标签后pod还在正常运行

    kubectl get pods -o wide | grep nodeselect
    nodeselector-pod                    1/1     Running            0          11m     10.244.2.47   k8s-node02   <none>           <none>
    

    把Pod删除然后再次创建Pod

    kubectl delete pods/nodeselector-pod
    
    kubectl apply -f nodeSelector-pod.yaml
    

    #会发现该pod一直在等待中,找不到清单中配置标签的Node

    kubectl get pods -o wide | grep nodeselect
    nodeselector-pod                    0/1     Pending            0          55s     <none>        <none>       <none>           <none>
    

    #事件:6个节点都不匹配 node selector

    kubectl describe pods/nodeselector-pod | grep -A 10 Events
    Events:
      Type     Reason            Age        From               Message
      ----     ------            ----       ----               -------
      Warning  FailedScheduling  <unknown>  default-scheduler  0/6 nodes are available: 6 node(s) didn't match node selector.
      Warning  FailedScheduling  <unknown>  default-scheduler  0/6 nodes are available: 6 node(s) didn't match node selector.
    

    污点与容忍度

    污点与容忍度(taint and tolerations)
    官方文档:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/#example-use-cases

    污点(taint)

    污点是非常霸道的行为,我们可以给Node打上污点,打污点的程度有三个级别,分别如下
    NoSchedule :为Node添加污点程度为NoSchedule,那么kube-scheduler将不在被调度Pod到本机。
    PreferNoSchedule:为Node添加污点程度为PreferNoSchedule,那么kube-scheduler将尽量不调度Pod到本机。
    NoExecute:为Node添加污点程度为NoExecute,那么kube-scheduler不仅不会调度Pod到本机,还会驱逐Node上已有的Pod。
    污点应用场景:节点独占,例如具有特殊硬件设备的节点,如GPU计算型硬件需要给特定的应用去使用。

    1.添加污点
    为k8s-node02添加污点,污点程度为NoSchedule,type=calculate为标签

    kubectl taint node k8s-node02 type=calculate:NoSchedule
    

    2.查看污点

     kubectl describe nodes k8s-node02 | grep Taints
    

    这样的话我们创建Pod就不会被调度到我们打上污点的k8s-node02的节点上

    3.创建Pod资源配置清单
    我们创建3个Pod,看看其是否会将Pod调度到我们打污点Node上

    cat taint-pod.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: taint-deploy
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: taint-pod
      template:
        metadata:
          labels:
            app: taint-pod
        spec:
          containers:
          - image: busybox:latest
            name: taint-pod
            command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    2.查看Pod被调度的Node
    下面三个Pod都被调度到了Node03上,效果可能不是很明显,我们为Node02打了污点,还有Node01没有体会出来

    kubectl apply -f taint-pod.yaml
    
    kubectl get pods -o wide | grep taint
    taint-deploy-748989f6d4-f7rbq       1/1     Running            0          41s     10.244.5.62   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-nzwjg       1/1     Running            0          41s     10.244.5.61   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-vzzdx       1/1     Running            0          41s     10.244.5.60   k8s-node03   <none>           <none>
    

    4.扩容Pod
    我们将Pod扩容至9台,让Pod分配到Node01节点,可以直观的展现污点

    kubectl scale --replicas=9 deploy/taint-deploy -n default
    
    kubectl get pods -o wide | grep taint
    taint-deploy-748989f6d4-4ls9d       1/1     Running            0          54s     10.244.5.65   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-794lh       1/1     Running            0          68s     10.244.5.63   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-bwh5p       1/1     Running            0          54s     10.244.5.66   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-ctknr       1/1     Running            0          68s     10.244.5.64   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-f7rbq       1/1     Running            0          2m27s   10.244.5.62   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-hf9sf       1/1     Running            0          68s     10.244.3.51   k8s-node01   <none>           <none>
    taint-deploy-748989f6d4-nzwjg       1/1     Running            0          2m27s   10.244.5.61   k8s-node03   <none>           <none>
    taint-deploy-748989f6d4-prg2f       1/1     Running            0          54s     10.244.3.52   k8s-node01   <none>           <none>
    taint-deploy-748989f6d4-vzzdx       1/1     Running            0          2m27s   10.244.5.60   k8s-node03   <none>           <none>
    

    以上调度了两台Pod到Node02,目前Node03和Node01都可以分配到Pod,而被打了污点的Node02无法分配Pod

    5.删除污点
    删除污点之需要指定标签的 key 及污点程度

    kubectl taint node k8s-node02 type:NoSchedule-
    

    容忍度(tolerations)

    上面为Node打了污点之后就没有Pod可以调度上去,那么我们必须要将专属的Pod给调度到打上污点的Node怎么办?那就给Pod添加上容忍度,被添加上容忍度的Pod则可以被调度到打了污点Node之上。

    容忍度所用到的参数tolerations,tolerations参数下的还有以下几个二级参数:

    operator:此值被称为运算符,值可以为[Equal|Exists],Equal表示污点的key是否等于value(默认参数),Exists只判断污点的key是否存在,使用该参数时,不需要定义value。
    effect:指定匹配的污点程度,为空表示匹配所有程度的污点,值可以为 [NoSchedule|PreferNoSchedule|NoExecut]。
    key:指定Node上污点的键key。
    value:指定Node上污点的值value。
    1.容忍度示例

    #为node03打上污点

    kubectl taint node k8s-node03 type=calculate:NoSchedule
    

    #查看污点

    kubectl describe nodes k8s-node03 | grep Taints
    Taints:             type=calculate:NoSchedule
    

    2.Pod资源配置清单

    cat taint-pod.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: taint-deploy
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: taint-pod
      template:
        metadata:
          labels:
            app: taint-pod
        spec:
          tolerations:
          - key: "type"
            operator: "Equal"
            value: "calculate"
            effect: "NoSchedule"
          containers:
          - image: busybox:latest
            name: taint-pod
            command: [ "/bin/sh", "-c", "tail -f /etc/passwd" ]
    

    3.创建Pod资源对象

    kubectl apply -f taint-pod.yaml
    

    4.查看Pod分配的Node
    以下两个Pod被调度到了Node03上,一个Pod被调度到了Node02上,我们做了容忍度是可以让Pod运行在被打了污点的Node上,但并不是所有打了容忍度的Pod都要运行在污点的Node上,所以 kube-scheduler 组件还会向其它Node进行调度Pod。

     kubectl get pods -o wide | grep taint
    taint-deploy-9868f98d7-dkr4n        1/1     Running            0          17s     10.244.5.74   k8s-node03   <none>           <none>
    taint-deploy-9868f98d7-f762b        1/1     Running            0          17s     10.244.5.75   k8s-node03   <none>           <none>
    taint-deploy-9868f98d7-zg4hk        1/1     Running            0          3m22s   10.244.2.49   k8s-node02   <none>           <none>
    

    5.为k8s-node01添加NoExecute驱赶程度的污点

    #添加污点

    kubectl taint node k8s-node01 type=data:NoExecute
    

    #观察Node01上的Pod状态

    kubectl get pods -o wide | grep k8s-node01
    

    下图中Node01上的Pod正在被驱赶至其它Node上进行重启
    img

    kubectl explain pods.spec.affinity

    Node亲和性调度

        参数文档:kubectl explain pods.spec.affinity.nodeAffinity

    亲和性调度机制极大的扩展了Pod的调度能力,主要增强功能如下:

    更具表达力,即更精细的力度控制;
    可以使用软限制、优先采用等限制方式,即调度器在无法满足优先需求的情况下,会使用其他次条件进行满足;
    可以依据节点上正在运行的其他Pod的标签来进行限制,而非节点本身的标签,从而实现Pod之间的亲和或互斥关系。
    目前有两种节点亲和力表达:

    requiredDuringSchedulingIgnoredDuringExecution:硬规则,必须满足指定的规则,调度器才可以调度Pod至Node上(类似nodeSelector,语法不同)。
    preferredDuringSchedulingIgnoredDuringExecution:软规则,优先调度至满足的Node的节点,但不强求,多个优先级规则还可以设置权重值。
    IgnoredDuringExecution指:如果一个Pod所在的节点在Pod运行期间标签发生了变化,不再符合该Pod的节点亲和性需求,则系统将忽略Node上Label的变化,该Pod能继续在该节点运行。

    Pod亲和性调度

        参数文档:kubectl explain pods.spec.affinity.podAffinity

    Pod互斥性调度

        参数文档:kubectl explain pods.spec.affinity.podAntiAffinity

    引自:
        本文链接:https://blog.csdn.net/qq_23435961/article/details/107914949
        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    展开全文
  • Pod 调度

    2018-06-19 21:11:41
    当我们创建一个deployment或者RC后,kuernetes会自动根据我们的要求将一个或多个Pod副本自动调度到合适的节点上,这个过程kube-scheduler经过一系列算法自动完成,用户无法干预。在某些场景,我们也可以使用...
  • Podfile.lock文件主要包含三个块:PODS、DEPENDENCIES、SPEC CHECKSUMS,用来记录每个pod的版本号、依赖的其他库和每个库对应的podspec.json文件的checksum(SHA-1算法)。通过这些信息可以确保多人协作的时候,大家...
  • Kubernetes之Pod

    2018-10-03 00:28:17
    Pod是Kubernetes对象模型中能够创建或部署的最小并且是最简单的基本单元。一个Pod代表在集群中正在运行的一个进程。 Pod由一个或多个容器组成,它们共享容器存储、网络和容器运行配置项。Pod中的容器总是被同时调度...
  • Kubernetes之Pod调度

    2017-10-16 15:37:45
    【编者的话】Kubernetes调度器根据特定的算法与策略将pod调度到工作节点上。在默认情况下,Kubernetes调度器可以满足绝大多数需求,例如调度pod到资源充足的节点上运行,或调度pod分散到不同节点使集群节点资源均衡...
  • 预选算法 CheckNodeConditionPred 检测节点是否处于就绪截断 GeneralPred 检查节点上pod资源对象数量的上线,以及CPU 内存 GPU等资源是否符合要求 NoDiskConflictPred 检查当前pod资源对象使用的卷...
  • Pod生命周期管理 1.1 Pod生命周期 Pod在整个生命周期过程中被系统定义了如下各种状态。 状态值 描述 Pending API Server已经创建该Pod,且Pod内还有一个或多个容器的镜像...
  • Kubernetes--Pod

    2020-11-17 23:23:35
    目录1 pod简介 【前言】 参考文档 1 pod简介 pod是一个或多个容器的组合,这些容器共享存储、网络、命名空间和运行规范。pod相当于是这些容器的逻辑主机,容器有相同的共享上下文,所有容器被统一的安排和调度。 ...
  • Kubernetes Pod基础

    2021-02-08 10:40:43
    Kubernetes Pod基础 Kubernetes Pod 简介 Pod是Kubernetes集群运行的最小单元,每个Pod都有一个特殊的被称为"根容器"的Pause容器。Kubernetes为每个Pod都分配了一个Pod IP,一个Pod里的多个容器共享Pod IP地址。...
  • POD本征正交分解

    2015-10-08 17:05:19
    POD本征正交分解FOR PROPER ORTHOGONAL DECOMPOSITION OF VELOCITY DISTRIBUTIONS
  • pod CPU资源

    2021-02-24 16:13:41
    1.通过设置资源requests我们指定了pod对资源需求的最小值。调度器在将pod调度到节点的过程中会用到该信息。调度器在调度时只考虑那些未分配资源量满足pod 需求量的节点。如果节点的未分配资源量小于pod 需求量,这时...
  • 文章目录Kubernetes Pod 优先级和抢占怎么样使用优先级和抢占启用优先级和抢占PriorityClassPriorityClass 示例Pod priority抢占限制抢占(alpha 版本)饥饿式抢占PodDisruptionBudget is not supported低优先级 Pod...
  • Kubernetes_pod

    2019-12-29 20:19:43
    kubenetes pod的学习笔记
  • Horizontal Pod Autoscaler 根据观察到的CPU利用率(或在支持自定义指标的情况下,根据其他一些应用程序提供的指标)自动伸缩 replication controller, deployment, replica set, stateful set 中的pod数量。...
  • POD的调度

    2020-07-24 09:41:20
    POD亲和性的引入 在真实的生产环境中还存在如下所述的特殊需求 不同Pod之间的亲和性(Affinity):比如MySQL数据库与 Redis中间件不能被调度到同一个目标节点上,或者两种不同的Pod必须 被调度到同一个Node上,以...
  • Kubernetes基本概念(三)之Pod详解

    万次阅读 2017-09-28 16:27:36
    1. Pod的定义文件apiVersion: v1 kind: Pod metadata: name: string namaspace: string labels: - name: string annotations: - name: string spec: containers: - name: string images: string i
  • pod自动扩缩

    2021-03-03 14:52:51
    HPA示例 参考 可以根据 CPU 利用率自动扩缩 ReplicationController、 Deployment、ReplicaSet 或 StatefulSet 中的 Pod 数量
  • Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过执行kubectl scale命令或通过RESTful API对一个Deployment/RC进行Pod副本数量的设置。自动模式则需要用户根据某个性能指标或者自定义业务指标,...
  • 计算机常用算法对照表整理

    千次阅读 2017-07-26 10:58:01
    中文名称条件随机场算法,外文名称conditional random field algorithm,是一种数学算法,是2001年提出的,基于遵循马尔可夫性的概率图模型。 全部对照第一部分、计算机算法常用术语中英对照 Data Structures 基本...
  • Kubernetes之pod调度

    2019-06-18 12:03:42
    Pod的调度在默认情况下是Scheduler Controller 采用默认算法的全自动调度,在实际使用中并不满足我们的需求,因为并不能事先掌握pod被调度到哪个Node之上,所以kubernetes又提供了好几种方式让我们自已选择调度到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,612
精华内容 4,644
关键字:

pod算法