k8s Pod 与 Deployment 各种 yaml 操作

602 阅读14分钟
#应⽤
kubectl apply -f /etc/k8s/nginx-pod.yaml
#移除
kubectl delete -f /etc/k8s/nginx-pod.yaml
#API版本号,固定写v1即可
apiVersion: v1
#创建对象类型,Pod代表该YAML要创建⼀个pod
kind: Pod
#元数据,描述pod的辅助信息,后⾯⽤于
metadata:
 #k8s管理pod的名称
 name: pod-nginx
#Spec⽤于设置容器、镜像等关键选项
spec:
 #配置容器信息
 containers:
   #容器名称
 - name: container-nginx
 	 #镜像名称
 	 image: nginx:latest
 	 #容器内部暴露的端⼝号,即expose
 	 ports:
 	 #nginx容器默认对外暴露80端⼝
 	 - containerPort: 80
#查看已部署的Nginx节点  -A 所有命名空间  wide 更详细的信息
kubectl get pods -A -o wide
#直接删除POD,不推荐
kubectl delete pod pod-nginx

#查看指定pod明细
kubectl describe pod pod-nginx
#查看输出的⽇志  -f 新日志会直接看到
kubectl logs -f pod-nginx
crictl ps

POD生命周期与重启、拉取策略

Kubernetes 中, pod 从创建到成功运⾏会分别处于不同的阶段,每个阶段⼜分为不同的状态,本⽂将简单介绍这个环节。

在 K8S 源码中,使⽤了 PodPhase 这个变量定义这⼏个阶段信息,如 下图所示:

K8S 中 Pod ⽣命周期

  • 运⾏中(Running):该 Pod 已经绑定到了⼀个节点上,Pod 中 所有的容器都已被创建。⾄少有⼀个容器正在运⾏,或者正处于启动或重启状态。
  • 等待中(Pending): 创建 Pod 的请求已被 Kubernetes 系统接受,但有⼀个或者多个容器镜像尚未创建。可能的原因有,写数据到 etcd,调度,pull镜像,启动容器这四个阶段中的任何⼀个阶段,pending 伴随的事件通常会有:ADDED, Modified这两个事件的产⽣。等待时间包括调度 Pod 的时间和下载镜像的时间,这可 能需要花些时间。
  • 正常终⽌(Succeeded):pod 中的所有的容器已经正常的⾃⾏退出,并且k8s 永远不会⾃动重启这些容器,⼀般会是在部署 job 的时候会出现。
  • 异常停⽌(Failed):Pod 中的所有容器都已终⽌了,并且⾄少有⼀个容器是因为失败终⽌。也就是说,容器以⾮ 0 状态退出或者被系统终⽌。 未知状态(PodUnkonwn):出于某种原因,⽆法获得 Pod 的状态,通常是由于与Pod 主机通信时出错。

Pod 的详细状态

  • CrashLoopBackOff: 容器退出,kubelet 正在将它重启,建议增加内存、CPU 等资源
  • InvalidImageName: ⽆法解析镜像名称
  • ImageInspectError: ⽆法校验镜像
  • ErrImageNeverPull: 策略禁⽌拉取镜像
  • ImagePullBackOff: 正在重试拉取,建议更换镜像仓库 RegistryUnavailable: 连接不到镜像中心
  • ErrImagePull: 通⽤的拉取镜像出错
  • CreateContainerConfigError:不能创建 kubelet 使⽤的容器配置
  • CreateContainerError: 创建容器失败
  • m.internalLifecycle.PreStartContainer 执⾏ hook 报错
  • RunContainerError: 启动容器失败
  • PostStartHookError: 执⾏ hook 报错
  • ContainersNotInitialized: 容器没有初始化完毕
  • ContainersNotReady: 容器没有准备完毕
  • ContainerCreating:容器创建中,⻓时间卡死要 decribe 与 logs 查看⽇志分析内容
  • PodInitializing:pod 初始化中 DockerDaemonNotReady:docker 还没有完全启动
  • NetworkPluginNotReady: ⽹络插件还没有完全启动

POD重启策略

pod的重启策略有 3 种,如下:

  • Always:容器失效时,⾃动重启该容器,这是默认值
  • OnFailure:容器停⽌运⾏且退出码不为 0 时重启
  • Never:不论状态为何,都不重启该容器

重启策略适⽤于 pod 对象中的所有容器,⾸次需要重启的容器,将在其需要时⽴即进⾏重启,随后再次需要重启的操作将由 kubelet 延迟⼀段 时间后进⾏,且反复的重启操作的延迟时⻓为 10s,20s,40s,80s, 160s,300s,300s 是最⼤延迟时⻓

apiVersion: v1
kind: Pod
metadata:
	name: pod-nginx
spec:
	containers:
	- name: container-nginx
		image: nginx:latest
		ports:
		- containerPort: 80
	#定义重启策略
	restartPolicy: Never

POD镜像拉取策略

  • IfNotPresent:默认值,镜像在宿主机上不存在时才拉取(⾯对稳定版本)
  • Always:每次创建 Pod 都会重新拉取⼀次镜像(⾯对不断变更版本)
  • Never: Pod 永远不会主动拉取这个镜像
apiVersion: v1
kind: Pod
metadata:
 name: pod-nginx
spec:
 containers:
 - name: container-nginx
 	 image: nginx:latest
 	 #镜像拉取策略
 	 imagePullPolicy: IfNotPresent
 	 ports:
 	 - containerPort: 80
 restartPolicy: Never

利用探针实现POD健康检查

Pod的三种探针类型

  • StartupProbe:k8s1.16 版本后新加的探测⽅式,⽤于判断容器内应⽤程序是否已经启动。如果配置了startupProbe,就会先禁止其他的探测,直到它成功为止,成功后将不再进⾏探测。⽐较适⽤于容器启动时间⻓的场景。
  • LivenessProbe(存活探针):⽤于探测容器是否运⾏,如果探测失败,kubelet 会根据配置的重启策略进⾏相应的处理。若没有配置该探针,默认就是success。针对的是 pod 本身的健康状况。
  • ReadinessProbe(就绪探针):一般用于探测容器内的程序是否健康,它的返回值如果为 success,那么就代表这个容器已经完成启动,并且程序已经是可以接受流量的状态。

三者区别

  • ReadinessProbe 和 livenessProbe 可以使用相同探测方式,只是对 Pod 的处置方式不同
  • 存活探针是将检查失败的容器杀死,创建新的启动容器来保持 pod 正常工作,
    • 使⽤存活探针来确定什么时候要重启容器。
    • 例如,使用存活探针检查容器本身是否无响应、死锁, 有时候重启容器常常能解决此类问题,即使其中存在缺陷。
  • 就绪探针是,可以知道容器何时准备好接受请求流量
    • 当 ⼀个 Pod 内的所有容器都就绪时,才能认为该Pod 就绪。
    • 当就绪探针检查失败,并不重启容器,而是将 pod 移出服务,就绪探针确保服务中的 pod 都是可用的,若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除,确保客户端只与正常的 pod 交互并且客户端永远不会知道系统存在问题
    • 有些时候,应用程序临时不可用(加载大量数据或者依赖外部服务),这个时候,重启这个 Pod 无济于事,同时你也不希望请求被发送到该 Pod

Pod探针的四种检测方式

  • exec:在容器内执⾏⼀个命令,如果返回值为0,则认为容器健康。
  • tcpSocket:通过 TCP 连接检查容器内的端⼝是否通的,如果是通的就认为容器健康。
  • httpGet:通过应⽤程序暴露的 API 地址检查程序是否正常,如果状态码为 [200,400) 之间,则认为容器健康。(常用)
  • grpc:使⽤ gRPC 执⾏⼀个远程过程调⽤。 ⽬标应该实现 gRPC 健康检查。 如果响应的状态是 "SERVING",则认为诊断成功。 gRPC 探针是⼀个 Alpha 特性,只有在你启用了 "GRPCContainerProbe" 特性门控时才能使⽤。

探测结果

每次探测都将获得以下三种结果之⼀:

  • Success(成功):容器通过了诊断。
  • Failure(失败):容器未通过诊断。
  • Unknown(未知):诊断失败,因此不会采取任何⾏动。

配置范本

kubelet 会在容器启动 5 秒后发送第⼀个存活探针。 探针会尝试连接 nginx 容器的 80 端⼝。 如果探测成功,这个 Pod 会被标记为就绪状 态,kubelet 将继续每隔 10 秒运⾏⼀次探测。

此外,就绪探针 periodSeconds 字段指定了 kubelet 每隔 3 秒执⾏⼀ 次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执⾏第⼀次探 测前应该等待 3 秒。 kubelet 会向容器内运⾏的服务(服务在监听 8080 端⼝)发送⼀个 HTTP GET 请求来执⾏探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康 存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并 将其重启。返回⼤于或等于 200 并且⼩于 400 的任何代码都标示成功,其它返回代码都标示失败。

apiVersion: v1
kind: Pod
metadata:
 name: pod-nginx
spec:
 containers:
 - name: container-nginx
 	 image: nginx:latest
 	 ports:
 	 - containerPort: 80
 	 #配置存活探针,每五秒钟执⾏⼀次探测容器80端⼝是否准备就绪
 	 #⽽第⼀次探测执⾏前先等待10秒,留出必要的初始化时间
 	 livenessProbe:
 		 tcpSocket:
 			 #port: 8080
 			 port: 80
     initialDelaySeconds: 10
 		 periodSeconds: 5
 readinessProbe:
 	 httpGet:
 		 path: /abcde
 		 #path: /
 		 port: 80
 		 httpHeaders:
 			 - name: Custom-Header
 			 value: Awesome
   initialDelaySeconds: 3
 	 periodSeconds: 3 

Pod如何对外暴露

暴露Pod及Service的6种方式

利用 hostNetwork 选项暴露端口

⼀次性可以暴露多个端口,且与容器端口保持⼀致,所以一个 node 只能部署一个 pod ,多个会出现端口冲突

#API版本号,固定写v1即可
apiVersion: v1
#创建对象类型,Pod代表该YAML要创建⼀个pod
kind: Pod
#元数据,描述pod的辅助信息,后⾯⽤于
metadata:
  #k8s管理pod的名称
  name: pod-nginx-0
#Spec⽤于设置容器、镜像等关键选项
spec:
  hostNetwork: true
  #部署在哪个节点上
  nodeName: node0
  #配置容器信息
  containers:
    #容器名称
    - name: container-nginx
      #镜像名称
      image: nginx:latest
      #容器内部暴露的端⼝号,即expose
      ports:
      #nginx容器默认对外暴露80端⼝
      - containerPort: 80

# --- 分割,可以创建多个pod
---
#API版本号,固定写v1即可
apiVersion: v1
#创建对象类型,Pod代表该YAML要创建⼀个pod
kind: Pod
#元数据,描述pod的辅助信息,后⾯⽤于
metadata:
  #k8s管理pod的名称
  name: pod-nginx-1
  #Spec⽤于设置容器、镜像等关键选项
spec:
  hostNetwork: true
  nodeName: node1
  #配置容器信息
  containers:
    #容器名称
    - name: container-nginx
      #镜像名称
      image: nginx:latest
      #容器内部暴露的端⼝号,即expose
      ports:
        #nginx容器默认对外暴露80端⼝
        - containerPort: 80

利用 hostPort 选项暴露端口

需要手动声明对外暴露的端口号,通过 PpdIP+containerPort 和 node节点 IP+hostPort 都可以正常访问到

#API版本号,固定写v1即可
apiVersion: v1
#创建对象类型,Pod代表该YAML要创建⼀个pod
kind: Pod
#元数据,描述pod的辅助信息,后⾯⽤于
metadata:
  #k8s管理pod的名称
  name: pod-nginx-0
#Spec⽤于设置容器、镜像等关键选项
spec:
  nodeName: node0
  #配置容器信息
  containers:
    #容器名称
    - name: container-nginx
      #镜像名称
      image: nginx:latest
      #容器内部暴露的端⼝号,即expose
      ports:
        #nginx容器默认对外暴露80端口,用 8000端口访问会映射到80
        - containerPort: 80
          hostPort: 8000
        - containerPort: 443
          hostPort: 8443

---
#API版本号,固定写v1即可
apiVersion: v1
#创建对象类型,Pod代表该YAML要创建⼀个pod
kind: Pod
#元数据,描述pod的辅助信息,后⾯⽤于
metadata:
  #k8s管理pod的名称
  name: pod-nginx-1
#Spec⽤于设置容器、镜像等关键选项
spec:
  nodeName: node1
  #配置容器信息
  containers:
    #容器名称
    - name: container-nginx
      #镜像名称
      image: nginx:latest
      #容器内部暴露的端⼝号,即expose
      ports:
        #nginx容器默认对外暴露80端⼝
        - containerPort: 80
          hostPort: 8000
        - containerPort: 443
          hostPort: 8443

hostPort 与 hostNetwork 异同点

  • 相同点
    • hostPort 与 hostNetwork 本质上都是暴露 pod 所在节点 IP 给终端用户,因为 pod 生命周期并不固定,随时都有可能异常重建,故 IP 的不确定最终导致用户使用上的不方便;此外宿主机端口占用也导致不能在同一台机子上有多个程序使用同一端口。因此一般情况下,不建议使用 hostPort 方式。
  • 不同点
    • 使用 hostNetwork,pod 实际上用的是 pod 宿主机的网络地址空间:即 pod IP 是宿主机 IP,而非 cni 分配的 pod IP,端口是宿主机网络监听接口。
    • 使用 hostPort,pod IP 并非宿主机 IP,而是 cni 分配的 pod IP,跟其他普通的 pod 使用一样的 ip 分配方式,端口并非宿主机网络监听端口,只是使用了 DNAT 机制将 hostPort 指定的端口映射到了容器的端口之上(可以通过 iptables 命令进行查看)。外部访问此 pod 时,仍然使用宿主机和 hostPort 方式。

YAML 构建 Deployment 部署脚本

apiVersion: apps/v1
#kind告诉Kubernetes现在定义的是⼀个Deployment对象
kind: Deployment
#metadata部分定义Deployment的名字和标签
metadata:
  #部署名称
  name: deploy-nginx

spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  #spec.template下的内容定义了Deployment管理的Pod模板
  template:
    #元数据定义每⼀个POD拥有标签 key=app,value=nginx
    metadata:
      labels:
        app: nginx
    #template.spec说明具体部署的容器与镜像信息
    spec:
      containers:
        #容器名
        - name: container-nginx
        #镜像名称
          image: nginx:1.20.2-alpine
          #容器对外暴露的端⼝号
          ports:
          - containerPort: 80

应用之后查看

kubectl get pods -o wide --show-labels

滚动更新策略与设置

Kubernetes ⽀持 RollingUpdate 策略以逐渐⽤新的 pod 替换旧的 pod,同时继续为客户端提供服务而不会导致停机。要执⾏滚动更新部署:

执行以下命令成功保存后就会进行更新

# kubectl apply 每次更新应用时 Kubernetes 都会记录下当前的配置,
# 保存为一个 revision(版次),这样就可以回滚到某个特定 revision。
# 默认配置下,Kubernetes 只会保留最近的几个 revision,可以在 
# Deployment 配置文件中通过 revisionHistoryLimit 属性增加 revision 数量。
# 使用也很简单,在更新的时候加上--record就可以了。
kubectl edit deploy deploy-nginx --record

或者

vim ./deploy-nginx.yaml
kubectl apply -f ./deploy-nginx.yaml --record
    • 滚动更新期望状态是 Pod 数量为 4 个副本,那么 maxSurge:0 的意思是在更新过程中,Pod 数量不能超过 4个,而 maxUnavailable:1 的意思同⼀时间不可⽤状态 POD 数量最多为 1 个。导致的结果就是,在滚动更新的过程中,最多只能同时更新 1 个 Pod(4-3=1)
    • 如果 maxSurge:1的意思是在更新过程中,Pod 数量不能超过 4+1=5 个, 而 maxUnavailable:1 的意思同⼀时间可⽤状态 POD 数量最多为 4-1=3 个。导致的结果就是,在滚动更新的过程中,最多只能同时更新 2 个 Pod(5-3=2)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  selector:
    matchLabels:
      app: nginx
      #滚动更新动作间隔10s
  minReadySeconds: 10
  strategy:
        #使⽤滚动更新策略升级
        type: RollingUpdate
        rollingUpdate:
          #最多允许出现1个不可⽤pod
          maxUnavailable: 1
          #不允许溢出,Pod总量最多只能4个
          maxSurge: 0
  replicas: 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: container-nginx
          image: nginx:1.20.2-alpine
          ports:
            - containerPort: 80
kubectl rollout status deploy deploy-nginx

部署 Pod 的扩容和缩容

scale 实现静态扩容与缩容

kubectl scale deploy deploy-nginx --replicas=4
kubectl get pods

默认配置使用的是 kube-scheduler 调度组件,是通过过滤和打分后判断启动在哪个 node 上,并不是均匀分布的。

回滚到指定版本

命令(edit apply 等)后加 --record 才会记录版本

# 查看指定 deployment 版本
kubectl rollout history deploy deploy-nginx
# 回退到指定版本
kubectl rollout undo deploy deploy-nginx -to-revision=3
#yaml格式看所有历史版本yaml格式信息
kubectl rollout history deploy deploy-nginx -o yaml
#yaml格式看指定历史版本yaml格式信息
kubectl rollout history deploy deploy-nginx -o yaml --revision=3
#yaml格式导出指定历史版本yaml格式信息
kubectl rollout history deploy deploy-nginx -o yaml --revision=3 > deploy-nginx-version-3.yaml

利用 DaemonSet 为新节点自动部署 pod

DaemonSet

为每一个工作 node 自动创建唯一的 pod,由 DaemonSet 来维护。 DaemonSet 确保全部(或者某些)节点上运⾏⼀个 Pod 的副本。 当有节点加⼊集群时, 也会为他们新增⼀个 Pod 。 当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所 有 Pod。

DaemonSet 的⼀些典型⽤法:

  • 在每个节点上运⾏集群守护进程
  • 在每个节点上运⾏⽇志收集守护进程
  • 在每个节点上运⾏监控守护进程