• 06 . Kubernetes之Pod控制器详细介绍及应用


    Pod API属性详解

    Pod是k8s集群中的最小编排单位。将这个设计落实到API对象上,容器就成了Pod属性里一个普通的字段。那么到底哪些属性属于Pod对象,哪些属性属于容器的呢?先看下面的一段描述:

    假如把Pod看成传统环境里的"机器"、那么容器就是运行在这个"机器"里的"用户程序",这样很多关于Pod对象的设计就非常容易理解了。凡是调度、网络、存储,以及安全相关的属性,基本上是Pod级别的。他们的共同特征是:描述的是"机器"这个整体,而不是里面运行的"程序"。比如:

    # 配置这个"机器"的网卡——Pod 的网络定义  
    # 配置这个"机器"的磁盘——Pod 的存储定义  
    # 配置这个"机器"的防火墙——Pod 的安全定义  
    # 这台"机器"运行在哪个服务器之上——Pod 的调度
    
    关于标签

    所有的资源都可以设置标签,目的就是为了给资源贴标识符,使用时用选择器调用标签,用到标签的地方:

    后端有以副本形式存在的Pod,这么多副本最好做负载均衡,此时就在前面创建一个service,一般情况下是先创建rc,然后创建service,创建完成后需要将service和后端的rc关联到一起,关联到一起就是用标签选择器关联的,即使不需要标签也最好设置一个能用的标签,标签可以由多个字典元素组成. 标签的格式: name:value,一个键值对表示一个标签,调用时一起调用,例如: 做nginx,这里是众多nginx的第一个,故可以设置这个标签由两个字典元素组成,标签可以有多行,但标签的值不能是纯数字.

    Pod级别的相关属性

    凡是跟Namespace都是Pod级别的,比如,容器的Linux Namespace、容器共享宿主机的 Namespace
    原因:Pod 的设计就是要让它里面的容器尽可能多地共享Linux Namespace,仅保留必要的隔离和限制能力。如此Pod模拟出的效果才能跟虚拟机里程序间的关系非常类似.

    apiVersion

    除了deployment是v1的升级版,其他的基本都是v1。 # kubectl api-versions

    kind

    指定这个API对象的资源类型: Pod、Deployment、Job、Ingress、Service等,资源类型的首字母需大写.

    metadata

    描述创建资源的属性,比如Pod的名称,namspace、标签等信息.

    spec

    specification of the resource content: 指定该资源的内容,包括一些container,storage,volume以及其他k8s需要的参数,以及诸如是否在容器失败时重新启动容器的属性,可在特定Kubernetes API找到完整的Kubernetes Pod属性.

    Spec常用字段

    # Pod资源:
    	spec.containers <[]object >
    spec:
      containers:
    - name <string>
      image <string>			# 仓库路径,项目名称,用户,镜像名称,镜像标签
      ports:
      - name: http
        containerPort: 80
      - name: https
        containerPort: 443
      imagePullPolicy: IfNotPresent	# 镜像获取策略
      
      # 修改镜像中的默认应用:
      	command,args
      # 标签<最多六十三个字符>
      	key=value
      		key: 字符,数字,_-,.,# 只能以字母数字开头及结尾
      		value: 可以为空,    # 只能字母或者数字开头及结尾,中间可使用:
      	# Always,Never,IfNotPresent
    # Always: 本地不管有没有镜像都是要到仓库去下载,本地无论有还是没有到要去仓库下载,就算本地有镜像他也不用: 
    # 虽然会导致容器启动变慢,但是可以防止被人恶意修改镜像不被中招:
    # Never: 永远不下载,需要用户手动去拖镜像,镜像一旦创建,不允许被更改,一旦编辑就报错,除非删除再创建:
    # IfNotPresent: 本地不存在就去下载:
    # 如果是latest标签就是Always,否则就是IfNotPresent 
    
    Example1(给Pod打标签示例)
    cat demo1-pod.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-demo
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
      - name: busybox
        image: busybox:latest
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        - "sleep 3600"
    
    查看Pod标签
    [root@master YAML]# kubectl get pods --show-labels
    NAME          READY   STATUS    RESTARTS   AGE     LABELS
    nginx-test2   1/1     Running   0          37m     run=nginx-test2
    pod-demo      2/2     Running   0          2m30s   app=myapp,tier=frontend
    
    [root@master YAML]# kubectl get pods -L app
    # 显示指定资源类别对象下所有标签的值. 
    NAME          READY   STATUS    RESTARTS   AGE   APP
    nginx-test2   1/1     Running   0          36m   
    pod-demo      2/2     Running   0          71s   myapp
      
    
    [root@master YAML]# kubectl get pods -l app  
    # -l才是过滤
    NAME       READY   STATUS    RESTARTS   AGE
    pod-demo   2/2     Running   0          84s
    
    修改标签
    # 如果期望将金丝雀改变为稳定版,觉得版本发布没问题,可以改过去
    kubectl  label pods pod-demo release=canar    	    
    
    # 标签不能重名,如果需要重名加上--overwrite覆盖即可.
    kubectl label pod pod-demo release=stable --overwrite
    
    # 修改节点标签
    kubectl label nodes node1 disktype=ssd
    
    标签选择器

    等值关系: =,==,!=都表示等值关系

    kubectl get pods -l release=stable,app --show-labels
    NAME       READY   STATUS    RESTARTS   AGE   LABELS
    pod-demo   2/2     Running   0          11m   app=myapp,release=stable,tier=frontend
    
    # 集合关系:
    # KEY in (VALUE1,VALUE2...)
    # KEY notin (VALUE1,VALUE2...)
    

    nodeSelector <map[string]string>能影响Pod调度算法

    如果我们需要对一些节点拥有ssd硬盘的机器做监控就需要将相应节点打上ssd标签.然后通过标签选择器将Pod运行在指定节点上.

    我们在之前那个demo-pod配置文件加上一个字段即可.

    我们先给节点打标签

    kubectl get nodes --show-labels  
    NAME     STATUS   ROLES    AGE   VERSION   LABELS  
    master   Ready    master   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=  
    node1    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux  
    node2    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux  
    
    kubectl label nodes node1 disktype=ssd  
    node/node1 labeled  
    
    kubectl get nodes --show-labels |grep disktype=ssd  
    node1    Ready    <none>   43h   v1.17.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
    

    我们重新修改下demo1-pod.yaml

    cat demo1-pod.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-demo
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
      - name: busybox
        image: busybox:latest
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        - "sleep 3600"
      nodeSelector:
        disktype: ssd
    kubectl create -f demo1-pod.yaml
    
    kubectl get pods -o wide
    kubectl get pods -o wide
    NAME          READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
    nginx-test2   1/1     Running   0          49m   10.244.0.18   master   <none>           <none>
    pod-demo      2/2     Running   0          53s   10.244.1.15   node1    <none>           <none>
    

    annotations:

    与label不同地方在于,他不能用于挑选资源对象,没有字符限制,仅用于对象提供“元数据”和注解.

    cat demo1-pod.yaml
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-demo
      namespace: default
      labels:
        app: myapp
        tier: frontend
      annotations:
        youmen.com/create-by: "youmen"
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443
      - name: busybox
        image: busybox:latest
        imagePullPolicy: IfNotPresent
        command:
        - "/bin/sh"
        - "-c"
        - "sleep 3600"
      nodeSelector:
        disktype: ssd
    
    kubectl create -f pod-demo.yaml
    kubectl describe pods pod-demo |grep Annotations
    Annotations:  youmen.com/create-by: youmen
    
    Pod生命周期

    Pod生命周期
    状态

    Pending:  # 调度尚未完成
    Running:  # 正在运行
    Failed:   # 失败
    Succeded: # 成功
    Unknown:  # 未知状态.
    

    创建Pod过程

    当用户创建Pod时,这个请求提交给API Server,他先将状态保存在etcd中,APIServer,接下来会请求Scheduler进行调度,如果调度成功后会将结果保存在etcd资源,更新到etcd状态资源中,随后目标节点调到node1上,根据那个清单创建所需要的Pod.

    Probe机制
    # Pod生命周期的重要行为:
    #	初始化容器:
    #	容器探测:
    #		1. lieness probe:  <是否存活>
    		# 如果检查失败,杀死容器,根据Pod的restartPolicy来操作
    #		2. readiness probe: <是否就绪>
    		# 如果检查失败,Kubernetes会把Pod从service endpoints中删除.
    
    #			两种都支持三种探测行为:
    #			1. exec: 执行自定义命令,看返回状态码是否为0
    #			2. tcpSocket: 向指定TCP套接字发请求,端口发请求,发起
    #			3. httpGet: 向指定http服务发请求,应用层get,url响应码.
    #				存活性探测: 主要用于判定主容器是否处于运行状态
    #				就绪性探测: 适用于判定容器中主进程是否处于就绪并可以对外进行服务.
    

    readiness probe:

    我们为动态有生命周期Pod提供一个固定的端点,service用标签选择器关联到各个Pod资源:

    此处有一个问题,如果有一个新的Pod,这个新Pod刚好符合service条件加入到后端,而有新请求进来就会被调度到新的Pod,但这个时候新Pod里面服务很有可能还没有就绪,里面文件还没有展开,而这个时候去访问就有可能出现访问失败,如果不做就绪性探测,Pod一创建就关联到service后出现大量访问失败:

    	restartPolicy: <重启策略>
    		Always,UnFailure,Never,Default to Always.
    		Always: # 一旦Pod中容器挂了就地将他重启.
    		UnFailure: # 状态为错误才重启,正常终止不重启: # 延时重启
    		Never: # 挂了就挂了,从来不重启.
    

    Example

    cat pod-probe.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-pod
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /index.html
            port: 80
    
    kubectl logs nginx-pod
    10.244.3.1 - - [22/Dec/2019:11:03:04 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
    10.244.3.1 - - [22/Dec/2019:11:03:14 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
    10.244.3.1 - - [22/Dec/2019:11:03:24 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
    10.244.3.1 - - [22/Dec/2019:11:03:34 +0000] "GET /index.html HTTP/1.1" 200 612 "-" "kube-probe/1.17" "-"
    # 我们可以看到每十秒都会进行一次测试
    # 接下来我们把那个页面删除,看会怎么样
    kubectl exec -it nginx-pod /bin/bash
    rm -rf usr/share/nginx/html/index.html
    # 然后我们去事件里面看看他做了什么处理.
    Events:
      Type     Reason     Age                  From               Message
      ----     ------     ----                 ----               -------
      Normal   Scheduled  3m35s                default-scheduler  Successfully assigned default/nginx-pod to node1
      Normal   Pulling    43s (x2 over 3m34s)  kubelet, node1     Pulling image "nginx"
      Warning  Unhealthy  43s (x3 over 63s)    kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404
      Normal   Killing    43s                  kubelet, node1     Container nginx failed liveness probe, will be restarted
      Normal   Pulled     21s (x2 over 3m14s)  kubelet, node1     Successfully pulled image "nginx"
      Normal   Created    21s (x2 over 3m14s)  kubelet, node1     Created container nginx
      Normal   Started    21s (x2 over 3m14s)  kubelet, node1     Started container nginx
    # 我们可以看到当检测不到报错的话,他会自己重新创建一个新镜像.此处只是个页面检测,
    # 如果是其他类型服务也可以使用TCP套接字做检测.
    
    Pod的排错
    kubectl describe TYPE  NAME_PREFIX
    kubectl  logs  nginx-xx
    kubectl  exec -it  nginx-xx bash or /bin/bash
    
    许多资源支持内嵌字段

    matchLabels: 直接给定键值
    matchExpresslons: 基于给定的表达式来定义使用标签选择器,{key:"KEY",operator: "OPERATOR" ,values: [VAL1,VAL2...]}

    # 操作符
    # In,NotIn: values字段的值必须为空列表.
    # Exists,NotExists: values字段的值为空列表.
    

    Pod控制器

    Pod控制器用于实现代我们管理Pod中间层,帮我们确保Pod是我们所期望的目标状态,如果出现故障,他会尝试重启,如果重启失败,他会尝试重新编排重新构建一个,如果Pod副本数量低于用户定义目标数量,他会自动补全,反之终止多余目标资源

    但此处Pod控制器只是一个泛称,包含以下控制器

    ReplicaSet控制器(ReplicationController)

    核心作用在于代用户创建指定数量的副本,并确保副本数量一直符合用户所定义状态,还支持扩缩容机制
    kubernetes不建议我们直接使用ReplicaSet,而是用Deployment,因为它还支持滚动更新,回滚,声明式配置更新
    简写为rs

    cat rs-demo1.yaml
    apiVersion: apps/v1
    kind: ReplicaSet
    metadata:
      name: myapp
      namespace: default
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: myapp
          release: canary 
      template:
        metadata: 
          name: myapp-pod
          labels:
            app: myapp
            release: canary
            environment: qa
        spec:
          containers:
          - name: myapp-container
            image: ikubernetes/myapp:v1
            ports:
            - name: http
              containerPort: 80
    # 我们此处的标签尽量条件匹配复杂一点,避免以后创建Pod标签冲突,多退少补
    # 如果需要添加副本数量直接edit配置文件即可,最好使用打补丁方式修改.
    # 如果需要更新版本,只有重建Pod才能更新成功.
    # 还能控制更新节奏,加一个删一个,加一个删两个,加两个删两个.d
    # 灰度发布: 删一个创建一个
    # 金丝雀发布: 删一个,等过段时间看用户反应,没什么不好的影响全部给升级
    # 蓝绿发布: 在创建个RS,改下service匹配条件,让请求都匹配到RS2上.
    # Deployment就建构在RS之上.一个Deployment可以管理多个RS,
    

    Deployment控制器

    kubernetes不建议我们直接使用ReplicaSet,而是用Deployment,因为它还支持滚动更新,回滚,声明式配置更新,管理无状态非常好

    cat pod-deployment.yaml 
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myapp-deploy
      namespace: default
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: myapp
          release: canary
      template:
        metadata:
          labels:
            app: myapp
            release: canary
        spec:
          containers:
          - name: myapp
            image: ikubernetes/myapp:v1
            ports:
            - name: http
              containerPort: 80
    
    kubectl get pods
    NAME                            READY   STATUS    RESTARTS   AGE
    myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          52s
    myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          52s
    # 此处的5msnk是模板的哈希值.我们可以通过apply增加副本数量,但是后面哈希值不变
    kubectl describe deploy myapp-deploy
    

    我们可以实时更新一下

    kubectl get pods -l app=myapp -w
    NAME                            READY   STATUS    RESTARTS   AGE
    myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          2m56s
    myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          2m56s
    # 我们再另开一个终端修改配置文件或者打补丁,然后实时查看此处的哈希变化.
    kubectl edit deploy myapp-deploy
    replicas: 4
    - image: ikubernetes/myapp:v2
    kubectl get pods -l app=myapp -w
    NAME                            READY   STATUS    RESTARTS   AGE
    myapp-deploy-7d574d56c7-44mqw   1/1     Running   0          8m15s
    myapp-deploy-7d574d56c7-5msnk   1/1     Running   0          8m15s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
    myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          0s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Pending   0          0s
    myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          0s
    myapp-deploy-798dc9b584-z7jhh   0/1     Pending   0          1s
    myapp-deploy-7d574d56c7-nkwt6   0/1     Pending   0          1s
    myapp-deploy-7d574d56c7-gvl9q   0/1     ContainerCreating   0          1s
    myapp-deploy-7d574d56c7-nkwt6   0/1     ContainerCreating   0          1s
    myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-q8fxd   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-z7jhh   0/1     ContainerCreating   0          1s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
    myapp-deploy-798dc9b584-q8fxd   0/1     ContainerCreating   0          0s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          1s
    myapp-deploy-7d574d56c7-nkwt6   1/1     Running             0          3s
    myapp-deploy-798dc9b584-z7jhh   1/1     Running             0          7s
    myapp-deploy-7d574d56c7-nkwt6   1/1     Terminating         0          7s
    myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-sjj65   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-sjj65   0/1     ContainerCreating   0          0s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
    myapp-deploy-7d574d56c7-gvl9q   0/1     Terminating         0          8s
    myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          8s
    myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
    myapp-deploy-7d574d56c7-nkwt6   0/1     Terminating         0          9s
    myapp-deploy-798dc9b584-sjj65   1/1     Running             0          2s
    myapp-deploy-7d574d56c7-44mqw   1/1     Terminating         0          9m13s
    myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-rdtmd   0/1     Pending             0          0s
    myapp-deploy-798dc9b584-rdtmd   0/1     ContainerCreating   0          0s
    myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m14s
    myapp-deploy-798dc9b584-q8fxd   1/1     Running             0          10s
    myapp-deploy-798dc9b584-rdtmd   1/1     Running             0          2s
    myapp-deploy-7d574d56c7-5msnk   1/1     Terminating         0          9m15s
    myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m16s
    myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
    myapp-deploy-7d574d56c7-5msnk   0/1     Terminating         0          9m17s
    myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s
    myapp-deploy-7d574d56c7-44mqw   0/1     Terminating         0          9m22s
    

    我们可以查看一下rs,此处有两个版本了,一个v1一个v2

    kubectl get rs -o wide
    NAME                      DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                 SELECTOR
    myapp-deploy-798dc9b584   4         4         4       4m15s   myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
    myapp-deploy-7d574d56c7   0         0         0       13m     myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary
    # 但是myapp:v1为历史版本,会保留着随时等待被回滚.
    
    # 可以使用下面命令进行版本回滚
    kubectl rollout history deployment myapp-deploy
    deployment.apps/myapp-deploy 
    REVISION  CHANGE-CAUSE
    1         <none>
    2         <none>
    

    我们可以使用patch打补丁方式修改实时更新策略

    kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
    # 此时副本数量就为5个了
    
    kubectl describe deployment myapp-deploy	# 修改deployment最大不可用和最小保留
    
    kubectl describe deployment myapp-deploy |grep RollingUpdateStrategy
    RollingUpdateStrategy:  0 max unavailable, 1 max surge
    # 接下来我们修改下镜像版本,然后更新看一看(金丝雀发布)
    kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy
    deployment.apps/myapp-deploy image updated
    deployment.apps/myapp-deploy paused
    # 另一个终端使用以下命令实时查看
    kubectl get pods -l app=myapp -w
    NAME                            READY   STATUS    RESTARTS   AGE
    myapp-deploy-798dc9b584-bcdxf   1/1     Running   0          14m
    myapp-deploy-798dc9b584-q8fxd   1/1     Running   0          57m
    myapp-deploy-798dc9b584-rdtmd   1/1     Running   0          57m
    myapp-deploy-798dc9b584-sjj65   1/1     Running   0          57m
    myapp-deploy-798dc9b584-z7jhh   1/1     Running   0          57m
    myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
    myapp-deploy-5dc9c974d7-qlpmq   0/1     Pending   0          0s
    myapp-deploy-5dc9c974d7-qlpmq   0/1     ContainerCreating   0          0s
    myapp-deploy-5dc9c974d7-qlpmq   1/1     Running             0          9s
    
    kubectl rollout status deployment myapp-deploy
    Waiting for deployment "myapp-deploy" rollout to finish: 1 out of 5 new replicas have been updated...
    # 此时我们发现他会一直卡在这一个Pod更新,接下来我们让他全部更新
    kubectl rollout resume deployment myapp-deploy
    
    # 我们发现此时有了第三个版本
     kubectl get rs -o wide
    NAME                      DESIRED   CURRENT   READY   AGE    CONTAINERS   IMAGES                 SELECTOR
    myapp-deploy-5dc9c974d7   5         5         5       5m4s   myapp        ikubernetes/myapp:v3   app=myapp,pod-template-hash=5dc9c974d7,release=canary
    myapp-deploy-798dc9b584   0         0         0       64m    myapp        ikubernetes/myapp:v2   app=myapp,pod-template-hash=798dc9b584,release=canary
    myapp-deploy-7d574d56c7   0         0         0       73m    myapp        ikubernetes/myapp:v1   app=myapp,pod-template-hash=7d574d56c7,release=canary
    
    # 接下来我们可以用回滚
    kubectl rollout undo deployment myapp-deploy --to-revision=1	# 回滚到第一个版本
    kubectl rollout history deployment myapp-deploy
    deployment.apps/myapp-deploy 
    REVISION  CHANGE-CAUSE
    2         <none>
    3         <none>
    4         <none>	# 此时我们看到已经有第四个版本了,而此时工作的是v1版
    curl 10.244.2.20
    Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
    
    DaemonSet控制器

    在集群中每个节点只运行一个Pod副本
    也可以根据自己需求,在部分节点运行一个Pod副本
    DaemonSet有以下特点

    # 1. 一般是无状态的
    # 2. 这种服务必须是守护进程,持续运行在后台
    

    Example1

    cat pod-ds.yaml 
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: redis
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: redis
          role: logstor
      template:
        metadata:
          labels:
            app: redis
            role: logstor
        spec:
          containers:
          - name: redis
            image: redis:4.0-alpine
            ports:
            - name: redis
              containerPort: 6379
    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: myapp-ds
      namespace: default
    spec:
      selector:
        matchLabels:
          app: filebeat
          release: stable
      template:
        metadata:
          labels:
            app: filebeat
            release: stable
        spec:
          containers:
          - name: filebeat
            image: ikubernetes/filebeat:5.6.5-alpine 
            env:
            - name: REDIS_HOST
              value: redis.default.svc.cluster.local
            - name: REDIS_LOG_LEVEL
              value: info
    
    kubectl exec -it redis-646cf89449-b5qkr /bin/bash
    Address 1: 10.96.249.167 redis.default.svc.cluster.local
    

    DaemonSet也支持滚动更新

    kubectl get ds -o wide
    NAME       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE   CONTAINERS   IMAGES                              SELECTOR
    myapp-ds   2         2         2       2            2           <none>          23m   filebeat     ikubernetes/filebeat:5.6.5-alpine   app=filebeat,release=stable
    [root@master ~]# kubectl set image daemonsets myapp-ds filebeat=ikubernetes/filebeat:5.6.6-alpine
    

    Example2 (Pod中容器和宿主机共享网络、进程通信和进程PID Namespace)

    cat ns1.yaml   
    apiVersion: v1  
    kind: Pod  
    metadata:  
     name: web-nginx2  
    spec:  
     hostNetwork: true  
     hostIPC: true  
     hostPID: true  
     containers:  
     - name: web-nginx2  
     image: daocloud.io/library/nginx  
     - name: shell  
     image: busybox  
     stdin: true  
     tty: true  
      
    kubectl apply -f ns1.yaml  
      
    kubectl attach -it web-nginx2 -c shell  
    / # ps ax |grep network  
     3522 root     15:32 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --  
    kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml   
    --cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1  
    23958 root      0:00 grep network  
      
    # Pod中的所有容器直接使用宿主机的网络、直接与宿主机进行IPC通信,且能够看到宿主机正在运行的所有进程  
    
    Job

    此类控制器主要为了完成指定任务、作业,只要没完成作业就会不断重建直至完成
    帮我们确保Pod任务是正常完成的,没有出现什么异常
    但是只是一次性任务
    也可以使周期性任务,但他们不需要持续在后台运行,CronJob

    以上控制器只能针对无状态应用,

    StatefulSet

    针对每一种服务每一种报错都要定义相应的恢复操作
    比如说你Mysql主从节点的从节点宕机了,你要定义很多操作创建一个从节点然后让他们衔接,稍有不慎,整个集群就崩溃了
    我们可以通过Operator将一些运维技能给灌进去,但是支持的应用有限
    直到后来出现了helm,类似于yum,类似于模板

    Example1(Pod中容器共用一个Pid Namespace)
    cat ns.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: web-nginx
    spec:
      shareProcessNamespace: true
      containers:
      - name: web-nginx
        image: daocloud.io/library/nginx
      - name: shell
        image: busybox
        stdin: true
        tty: trdue
    # 1、shareProcessNamespace: true表示此Pod中的容器共享PID,即每个容器都能看到其他容器内的进程  
    # 2. 定义两个容器:一个nginx;一个等同于使用-it选项的容器  
    此Pod被创建后,就可以使用shell容器的tty跟这个容器进行交互。
    
    kubectl apply -f ns.yaml
    
    kubectl attach -it web-nginx -c shell
    If you don't see a command prompt, try pressing enter.
    / # ps ax
    PID   USER     TIME  COMMAND
        1 root      0:00 /pause
        6 root      0:00 nginx: master process nginx -g daemon off;
       11 101       0:00 nginx: worker process
       21 root      0:00 sh
       26 root      0:00 ps ax
    # 在容器里能同时看到nginx和busybox以及Infra容器的/pause的进程,整个Pod里的每个容器进程对于所有容器来说都是可见的,
    # 因为它们共享同一个 PID Namespace。
    

    node的相关属性

    nodeName: 这个字段一般由调度器负责设置,用户也可以设置他来'骗过'调度器,一旦Pod的这个字段被赋值,k8s就会被认为此Pod已经调度过,调度的结果就是赋值的节点名字,这个做法一般是测试或者调试的时候才会用到,简单来说就是不让调度器去选择此Pod运行在哪个node上,而是用户自行指定该Pod运行的node.

    nodeSelector: 和nodeName的作用一样,是一个供用户将Pod与Node进行绑定的字段,例如: 设置Pod永远只能运行在携带了: "Kubernetes.io/hostname: k8s-node1"标签的节点上: 否则,他将调度失败.

    Example1(使用nodeSelector指定pod运行的节点)

    查看k8s-node1上的标签都有哪些

    kubectl describe pod web-nginx |grep ^Labels -C3
    Priority:     0
    Node:         node2/172.19.0.54
    Start Time:   Thu, 19 Dec 2019 17:45:43 +0800
    Labels:       <none>
    Annotations:  <none>
    Status:       Running
    IP:           10.244.1.5
    

    创建一个Pod指定运行在node1上

    cat nginx1.yaml
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: test-nginx
      labels:
        web: nginx2
        nginx: nginx3
    spec:
    #  nodeName: 192.168.122.5 #也可以使用下面的nodeSelector。
      containers:
        - name: web-test
          image: daocloud.io/library/nginx
          ports:
            - containerPort: 80
      nodeSelector: 
        kubernetes.io/hostname: node1 
    kubectl apply -f nginx1.yaml
    
    容器中的hosts文件解析

    HostAliases: 定义Pod容器中的/etc/hosts文件,解析主机名,且在k8s中,一定要通过此方法设置hosts文件中的内容,如果直接修改hosts文件,在Pod被删除重建之后,kubelet会自动覆盖被修改的内容

    cat nginx2.yaml 
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      name: web-nginx3
      labels:
        web: nginx4
        nginx: nginx5
    spec:
      hostAliases:
      - ip: "1.1.1.1"
        hostnames:
        - "cluster1.com"
        - "cluster2.com"
        - "cluster3.com"
      containers:
        - name: web2
          image: daocloud.io/library/nginx
          ports:
            - containerPort: 80
    
    kubectl apply -f nginx2.yaml 
    
    kubectl exec -it web-nginx3 /bin/bash
    root@web-nginx3:/# ls
    bin   dev  home  lib64	mnt  proc  run	 srv  tmp  var
    boot  etc  lib	 media	opt  root  sbin  sys  usr
    root@web-nginx3:/# cat /etc/hosts
    # Kubernetes-managed hosts file.
    127.0.0.1	localhost
    ::1	localhost ip6-localhost ip6-loopback
    fe00::0	ip6-localnet
    fe00::0	ip6-mcastprefix
    fe00::1	ip6-allnodes
    fe00::2	ip6-allrouters
    10.244.1.6	web-nginx3
    
    # Entries added by HostAliases.
    1.1.1.1	cluster1.com	cluster2.com	cluster3.com
    
    多个ip对应不同域名的写法
    spec:  
    hostAliases:  
    - ip: "1.1.1.1"  
    hostnames:  
    - "cluster1.com"  
    - ip: "2.2.2.2"  
    hostnames:  
    - "cluster2.com"
    

    容器的属性

    容器是Pod中最重要的字段,有"Containers" 和"Init Containers" 两种写法,内容完全相同.

    Init Containers: 其生命周期会先于所有的容器(即优先启动),并且严格按照定义的顺序执行.

    containers: 指定多个名称表示多个容器,每一个容器都是列表中的一个元素,每一列元素中还会有字典,字典不同的元素表示容器的不同属性,容器可以设置复杂的属性,包括容器启动运行的命令,使用的参数、工作目录以及每次实例化是否拉取新的副本,还可以指定更深入的信息,例如容器的退出日志的位置等. 包括: name、image、command、args、workingDir、ports、env、resource、volumeMounts、livenessProbe、readinessProbe、livecycle、terminationMessagePath、imagePullPolicy、securityContext、stdin、stdinOnce、tty

    name:容器的名字,起个有意义的名字  
    image:容器镜像  
    command:容器启动运行的命令  
    args:容器使用的参数  
    workingDir:容器的工作目录  
    ports:容器监听的端口  
    env:定义容器的环境变量,主要是给mysql这种应用使用的spec:containers:env  
      env:  
        - name: a  # 变量名称  
          
    value: "hello"  # 变量值,必须加引号  
    volumeMounts:# 共享卷  
    imagePullPolicy:# 镜像拉取策略,此属性有bug。  
    # 默认值Always表示每次创建Pod都重新拉取一次镜像,实际上是镜像在本地无需拉取;  
    Never:# 不拉取镜像,本地有直接使用,本地不存在就报异常错误  
    IfNotPresent: # 表示宿主机上不存在这个镜像时才拉取,建议使用。
    
    官方提供的钩子示例

    容器成功启动之后,在/usr/share/message中写入了一句“欢迎信息”(PostStart定义),在这个容器被删除之前,则先调用nginx的退出命令(preStop定义),从而实现容器中的应用从而关闭.

    cat message.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: lifecycle-demo
    spec:
      containers:
      - name: lifecycle-demo-container
        image: daocloud.io/library/nginx
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
          preStop:
            exec:
              command: ["/usr/sbin/nginx","-s","quit"]
    kubectl apply -f message.yaml
    

    注释

    postStart: 开始之后执行的动作,虽然理论上是在Docker容器ENTRYPOINT执行之后,但它并不严格保证顺序。即:在postStart启动时,ENTRYPOINT有可能还没有结束。如果postStart执行超时或者错误,k8s会在该Pod的Events中报出该容器启动失败的错误信息,导致Pod也处于失败的状态。

    preStop:停止之前指定的动作。操作的执行是同步的,所以它会阻塞当前的容器杀死流程,直到这个 Hook定义操作完成之后,才允许容器被杀死。

    Pod | docker 容器的生命周期

    Pod声明周期的变化,主要体现在Pod API对象的Status部分,这是除了Metadata和Spec之外的第三个重要字段,其中Pod.status.phase就是Pod的当前状态,有如下几种可能的情况:

    Pending: 此状态表示Pod的YAML文件已经提交给了kubernetes,API对象已经被创建并保存在Etcd中.但是这个Pod里有些容器因为某种原因不能被顺利创建,比如: 调度不成功. (准备状态: 准备好了就running,准备不好就是failed)

    # Running: 此状态表示Pod已经调度成功,跟一个具体的节点绑定,它包含的容器都已经创建成功,并且至少有一个正在运行中.
    # Succeeded: 此状态表示Pod里的所有容器都正常运行完毕,并且已经退出了,这种情况在运行一次性任务最为常见.
    # Failed: 此状态表示Pod里至少有一个容器以不正常的状态退出,这个状态出现以为这个你得Debug这个容器的应用,比如查看Pod的Events和日志.
    # Unknown: 这是一个异常状态,表示Pod的状态不能持续的kubelet汇报给kube-apiserver,这很有可能是主从节点(Master和Kubelet)之间的通信出现了问题.
    

    细分Pod对象的Status字段

    Pod对象的Status字段还可以细分一组Conditions,包括: PodScheduled、Ready、Initialized、以及Unschedulable,他们主要用于描述当前Status的具体原因,比如: Pod当前的Status是Pending,对应的Condition是Unschedulable,这表示他的调度出现了问题.

    投射数据卷Projected Volume

    是Kubernetes v1.11之后的新特性,在K8s中,有几种特殊的Volume,他们既不是存放容器里的数据,也不是用来和宿主机之间的数据交换,而是为容器提供预先定义好的为新建容器提供字符串的,字符串有长有短,就可以使用不同类型的投射数据卷,当然这个工作普通的volumn也可以实现,但是普通的volumn很容易暴露密码,token或者秘钥等敏感数据,而使用投射数据卷方式可以把敏感数据的资源直接存放在k8s集群的数据库中,可以更方便的控制这些数据,并减少暴露的风险,从容器的角度来看,这些Volume里的信息仿佛是被k8s"投射"(Project)进入容器当中的.

    k8s支持的Projected Volume

    # Secret: 设置密码可以使用
    # ConfigMap: 准备配置文件使用
    # Downward API: 设置环境变量
    # ServiceAccountToken: 属于一种特殊的Secret,做认证使用.
    
    # 使用时分两步:
    # 做出相应的投射数据卷存放到数据库
    # 创建容器时使用.
    

    Pod的重启策略

    三种重启策略
    Always:    # 当容器停止,总是重建容器,默认策略
    OnFailure:   # 当容器异常退出(退出状态码非0)时,才重启容器
    Never:   # 当容器终止退出,从不重启容器.
    
    Example
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-pod
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      restartPolicy: OnFailure
    
  • 相关阅读:
    每天进步一点点------Allegro 布线完成后如何修改线宽
    每天进步一点点------Allegro 布线时显示延迟以及相对延迟信息
    每天进步一点点------Allegro 修线
    每天进步一点点------Allegro 蛇形走线
    每天进步一点点------Allegro 动态显示走线长度
    每天进步一点点------Allegro 群组布线
    每天进步一点点------Allegro 手工布线时控制面板各选项说明
    每天进步一点点------Allegro 铺铜、内电层分割
    每天进步一点点------Allegro 铺铜详解
    每天进步一点点------Allegro使用脚本记录文件设置工作环境的颜色
  • 原文地址:https://www.cnblogs.com/you-men/p/13225016.html
Copyright © 2020-2023  润新知