GVKun编程网logo

k8s中controller部署有状态应用StatefulSet(k8s部署有状态服务)

6

此处将为大家介绍关于k8s中controller部署有状态应用StatefulSet的详细内容,并且为您解答有关k8s部署有状态服务的相关问题,此外,我们还将为您介绍关于07-K8S之Stateful

此处将为大家介绍关于k8s中controller部署有状态应用StatefulSet的详细内容,并且为您解答有关k8s部署有状态服务的相关问题,此外,我们还将为您介绍关于07-K8S之Statefulset控制器、k8s 1.25学习4 - 使用Deployment、StatefulSet部署应用、k8s StatefulSet、k8s StatefulSet控制器的有用信息。

本文目录一览:

k8s中controller部署有状态应用StatefulSet(k8s部署有状态服务)

k8s中controller部署有状态应用StatefulSet(k8s部署有状态服务)

k8s中controller部署有状态应用StatefulSet

  • 无状态和有状态区别
  • 部署有状态应用
    • 创建了三个有状态应用,查看pod,有三个,每个都是唯一的名称。
    • 查看创建的无头的service
    • deployment和statefulset区别
      • statefulset有唯一标识)
  • 部署守护进程DaemonSet
    • 实际操作
      • 编写yaml文件:
      • 删除StatefulSet创建的pod
      • 删除svc中的服务
      • 创建DaemonSet的pod
      • 进入某一个pod里面查看日志
  • Controller一次性任务JOB和定时任务CRONJOB
    • 一次性任务 Job
      • yaml文件
      • 创建job任务
      • 查看日志
      • 删除job的yaml文件
    • 定时任务
      • 编写yaml文件
      • 创建任务
      • 查看pod信息
      • 查看定时任务
      • 查看日志
      • 删除job

无状态和有状态区别

(1) 无状态:

  • 认为pod都是一样的(副本内容一样)
  • 没有顺序要求(先用哪一个pod)
  • 不用考虑在哪一个node运行
  • 随意进行伸缩和扩展
    (2)有状态:
  • 上面因素都要考虑到
  • 让每个pod都是独立的。保持pod的启动顺序和唯一性
    • 唯一的网络标识符,持久存储
  • 有序,比如MysqL主从

部署有状态应用

** 无头service也就是里面 ClusterIP:none**
无头的service

在这里插入图片描述

(1)SatefulSet 部署有状态应用

在这里插入图片描述

创建了三个有状态应用,查看pod,有三个,每个都是唯一的名称。

在这里插入图片描述

查看创建的无头的service

kubectl get pods,service
kubectl get pods,svc

在这里插入图片描述

deployment和statefulset区别

statefulset有唯一标识)

根据主机名+按照一定的规则生成域名
规则:
** 每个pod有唯一的主机名 **
** 唯一域名,格式为主机名称.service名称。名称空间.svc.cluster.local**
Nginx-statefulset-0.Nginx.default.svc.cluster.local

部署守护进程DaemonSet

它可以确保所有node运行同一个pod

在这里插入图片描述

  • 在每一个node上运行同一个pod, 新加入的node也同样运行同一个pod
    例子:在每个node节点安装数据采集工具(日志收集)

实际操作

编写yaml文件:

apiVersion: apps/v1
kind: DaemonSet
Metadata:
  name: ds-test
  labels:
    app: filebeat
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
      Metadata:
        labels:
          app: filebeat
      spec:
        containers:
        - name: logs
          image: Nginx
          ports:
          - containerPort: 80
          volumeMounts:
          - name: varlog
            mountPath: /tmp/log
        volumes:
        - name: varlog
          hostPath: 
            path: /var/log 

删除StatefulSet创建的pod

kubectl delete statefulset --all   //删除所有statefulset创建的pod

删除svc中的服务

kubectl delete svc [pod名称]  // 删除service中的pod服务

创建DaemonSet的pod

kubectl apply -f ds.yaml  // 创建 pod
kubectl get pods    //查看pods

可以看到创建完毕

在这里插入图片描述

进入某一个pod里面查看日志

kubectl exec -it [pod名称] /bin/bash
kubectl exec -it ds-test-fsl2w bash   //进入pod新的终端

在这里插入图片描述

Controller一次性任务JOB和定时任务CRONJOB

一次性任务 Job

yaml文件

apiVersion: batch/v1
kind: Job
Metadata: 
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl", "-Mbignum=bpi","-wle","print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

创建job任务

如果使用create创建job,无法进行滚动更新或者回滚,使用apply就可以

kubectl create -f job.yaml   // 如果使用`create`创建job,无法进行滚动更新或者回滚,使用`apply`就可以
kubectl get pods -o wide   //查看pod具体信息 ,可知道job在node2节点上
kubectl get jobs   //查看任务
docker images   // 在node2节点上可以查看到pull下来的perl镜像

创建了任务
而且只完成一次。出现completed

在这里插入图片描述

查看job的信息

在这里插入图片描述


node2节点的perl镜像被下载完成

在这里插入图片描述

查看日志

kubectl logs [pod名称] 

计算了pi,执行一次

在这里插入图片描述

删除job的yaml文件

在这里插入图片描述


再次查看job情况,会发现已经消失。

kubectl get jobs

在这里插入图片描述

定时任务

定时写出hello任务

编写yaml文件

[root@k8s-master01 ~]# cat cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
Metadata:
  name: hello
spec:
  schedule: "*/1****"
  jobTemplate:
  ##################CronJob#####################
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busyBox
              args:
              - /bin/sh
              - -c
              - date; echo He11o from the Kubernetes cluster
          restartPolicy: OnFailure

创建任务

kubectl apply -f cronjob.yaml 

在这里插入图片描述

查看pod信息

发现每隔一分钟执行一次hello

在这里插入图片描述

查看定时任务

kubectl get coronjobs

在这里插入图片描述

查看日志

kubectl logs [job名称]
kubectl logs hello-27242448--1-mm9kj     // 查看日志

在这里插入图片描述

删除job

kubectl delete cornjob [名称]   //删除job
kubectl delete cronjob  hello   // 删除了定时任务hello

在这里插入图片描述

07-K8S之Statefulset控制器

07-K8S之Statefulset控制器

目录
  • Statefulset
    • 1.Statefulset****概念
    • 2.学习达到的目标
    • 3.部署一个web应用
      • 1.通过statefulset部署pod,并且观察pod创建的过程
      • 2.顺序创建 Pod
      • 3.StatefulSet 中的 Pod
      • 4.检查 Pod 的顺序索引
      • 5.使用稳定的网络身份标识
      • 6.导入稳定的存储
    • 4.部署和扩缩容
    • 5.Statefulset的资源清单yaml文件书写规范
    • 6.Pod 管理策略
    • 7.OrderedReady Pod 管理
    • 8.Parallel Pod 管理
    • 9.更新策略
    • 10.关于删除策略
    • 11.滚动更新
    • 12.分区
    • 13.强制回滚
    • 14.statefulSet 控制器搭建Redis:
    • 15.statefulSet控制器搭建etcd集群:

Statefulset

1.Statefulset****概念

StatefulSet是为了解决有状态服务的问题(对应Deployments和replicasets是为无状态服务)而设计,其应用场景包括
1.稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
2.稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
3.有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前的所有Pod必须都是Running和Ready状态),基于init containers来实现
4.有序收缩,有序删除(即从N-1到0)
5.有序的滚动更新
从上面的应用场景可以发现,StatefulSet由以下几个部分组成:

1.通过Headless Service生成可解析的DNS记录
2.通过volumeClaimTemplates创建pvc和对应的pv绑定
3.定义StatefulSet来创建pod
和Deployment相同的是,StatefulSet管理了基于相同容器定义的一组 Pod。但和 Deployment不同的是,StatefulSet 为它们的每个 Pod 维护了一个固定的ID。这些 Pod 是基于相同的声明来创建的,但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的ID。StatefulSet和其他控制器使用相同的工作模式。你在StatefulSet对象中定义你期望的状态,然后StatefulSet的控制器就会通过各种更新来达到那种你想要的状态。

StatefulSet中每个Pod的DNS格式为statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中
1.serviceName为Headless Service的名字
2.0..N-1为Pod所在的序号,从0开始到N-1
3.statefulSetName为StatefulSet的名字
4.namespace为服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace
5..svc.cluster.local为Cluster Domain

2.学习达到的目标

1.如何创建StatefulSet?
2.通过StatefulSet怎样管理它的Pod?
3.如何删除 StatefulSet?
4.如何对StatefulSet进行扩容/缩容?
5.如何更新一个 StatefulSet的Pod

3.部署一个web应用

作为开始,使用如下示例创建一个StatefulSet。它和StatefulSets概念中的示例相似。它创建了一个Headless Service的Nginx用来发布 StatefulSet web中的Pod的IP地址。

[root@master01 statefulset-controller]# cat statefulset-pod.yaml 
kubectl apply -f statefulset-pod.yaml

apiVersion: v1
kind: Service
Metadata: 
  name: Nginx
  labels:
     app: Nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: ""
  selector:
    app: Nginx
---
apiVersion: apps/v1
kind: StatefulSet
Metadata: 
  name: web
spec:
  selector:
    matchLabels:
      app: Nginx
  serviceName: "Nginx"
  replicas: 2
  template:
    Metadata: 
     labels:
       app: Nginx
    spec: 
      containers:
      - name: Nginx
        image: Nginx:1.20
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/Nginx/html
  #    - name: busyBox
  #      image: busyBox:latest
  #     command: ["/bin/sh","-c","sleep 3600"]
  volumeClaimTemplates:
  - Metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests: 
          storage: 2Gi
注:为什么要使用volumeClaimTemplate
对于有状态应用都会用到持久化存储,比如MysqL主从,由于主从数据库的数据是不能存放在一个目录下的,每个MysqL节点都需要有自己独立的存储空间。而在deployment中创建的存储卷是一个共享的存储卷,多个pod使用同一个存储卷,它们数据是同步的,而statefulset定义中的每一个pod都不能使用同一个存储卷,这就需要使用volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,每一个pod都有自己专用的存储卷。Pod、PVC和PV对应的关系图如下:

1.通过statefulset部署pod,并且观察pod创建的过程

你需要使用两个终端窗口。在第一个终端中,使用kubectl get来查看 StatefulSet的Pods的创建情况。
kubectl get pods -w -l app=Nginx
在另一个终端中,使用 kubectl apply -f statefulset.yaml来创建定义在statefulset.yaml中的 Headless Service 和StatefulSet。
kubectl apply -f statefulset.yaml 

上面的命令创建了两个 Pod,每个都运行了一个Nginx web 服务器。获取Nginx Service 和web StatefulSet来验证是否成功的创建了它们。

查看service
kubectl get service Nginx 
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 
Nginx None <none> 80/TCP 12s 

查看statefulset 
kubectl get statefulset web 

显示如下:
NAME   READY   AGE
web    2/2     20m

2.顺序创建 Pod

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0..N-1}的序号顺序创建的。在第一个终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子。
kubectl get pods -w -l app=Nginx
NAME READY STATUS RESTARTS AGE
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 19s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 18s
请注意在web-0 Pod处于Running和Ready 状态后web-1 Pod才会被启动。

3.StatefulSet 中的 Pod

StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识

4.检查 Pod 的顺序索引

获取 StatefulSet 的 Pod。
kubectl get pods -l app=Nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          1m
web-1     1/1       Running   0          1m

如同StatefulSets 概念中所提到的, StatefulSet中的Pod拥有一个具有黏性的、独一无二的身份标志。这个标志基于StatefulSet控制器分配给每个 Pod的唯一顺序索引。 Pod的名称的形式为<statefulset name>-<ordinal index>。web StatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0 和 web-1

5.使用稳定的网络身份标识

每个Pod都拥有一个基于其顺序索引的稳定的主机名(statefulset创建的pod的主机名由statefulset的名称和有序索引组成)。使用kubectl exec 在每个 Pod 中执行hostname
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done 
显示如下:
web-0 
web-1

使用kubectl run运行一个提供nslookup命令的容器,该命令来自于dnsutils包。通过对Pod的主机名执行nslookup,你可以检查他们在集群内部的DNS地址。
kubectl run -i --tty --image busyBox:latest dns-test --restart=Never --rm /bin/sh
nslookup web-0.Nginx.default.svc.cluster.local.

kubectl exec -it web-1 -- /bin/bash

apt-get install dnsutils -y 

nslookup web-0.Nginx.default.svc.cluster.local

或者在容器外部使用dig解析:
dig -t A web-0.Nginx.default.svc.cluster.local @10.96.0.10

在一个终端中查看 StatefulSet 的 Pod。
kubectl get pod -w -l app=Nginx
在另一个终端中使用 kubectl delete 删除 StatefulSet 中所有的 Pod。
kubectl delete pod -l app=Nginx
pod "web-0" deleted
pod "web-1" deleted
等待 StatefulSet 重启它们,并且两个 Pod 都变成 Running 和 Ready 状态。
kubectl get pod -w -l app=Nginx
NAME      READY     STATUS              RESTARTS   AGE
web-0     0/1       ContainerCreating   0          0s
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          2s
web-1     0/1       Pending   0         0s
web-1     0/1       Pending   0         0s
web-1     0/1       ContainerCreating   0         0s
web-1     1/1       Running   0         34s

使用 kubectl exec 和 kubectl run 查看 Pod 的主机名和集群内部的 DNS 表项
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done
web-0
web-1
web-2

kubectl exec -it  web-1 -- /bin/bash
apt-get  install  dnsutils -y     

#安装nslookup命令的的

nslookup  web-0.Nginx.default.svc.cluster.local

Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod的IP 地址进行连接,这点很重要。如果你需要查找并连接一个 StatefulSet的活动成员,你应该查询Headless Service 的CNAME。CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。如果你的应用已经实现了用于测试 liveness 和 readiness 的连接逻辑,你可以使用 Pod 的 SRV 记录(web-0.Nginx.default.svc.cluster.local, web-1.Nginx.default.svc.cluster.local)。因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。

域名对应的表格:

6.导入稳定的存储

获取web-0 和 web-1 的 PersistentVolumeClaims
kubectl get pvc -l app=Nginx
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    v1       1Gi        RWO                           50m
www-web-1   Bound    v4       2Gi        RWO,RWX                       50m
www-web-2   Bound    v5       5Gi        RWO,RWX                       50m
StatefulSet 控制器创建了三个 PersistentVolumeClaims,绑定到三个PersistentVolumes。
Nginx web 服务器默认会加载位于 /usr/share/Nginx/html/index.html 的 index 文件。StatefulSets spec 中的 volumeMounts 字段保证了 /usr/share/Nginx/html 文件夹由一个 PersistentVolume 支持。
将Pod的主机名写入它们的index.html文件并验证 Nginx web 服务器使用该主机名提供服务。

for i in 0 1; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/Nginx/html/index.html'; done

for i in 0 1; do kubectl exec -it web-$i -- curl localhost; done 
web-0 
web-1
请注意,如果你看见上面的 curl 命令返回了 403 Forbidden 的响应,你需要像这样修复使用 volumeMounts (due to a bug when using hostPath volumes)挂载的目录的权限:
for i in 0 1 2; do kubectl exec web-$i -- chmod 755 /usr/share/Nginx/html; done
在你重新尝试上面的 curl 命令之前。

4.部署和扩缩容

kubectl scale sts web --replicas=4
kubectl patch sts myapp -p ‘{“spec”:{“replicas”:2}}’ # 通过打补丁的方式扩缩容。
1.对于包含N个副本的StatefulSet,当部署Pod时,它们是依次创建的,顺序为 0..N-1。
2.当删除 Pod 时,它们是逆序终止的,顺序为 N-1..0。
3.在将缩放操作应用到Pod之前,它前面的所有Pod 必须是 Running 和 Ready 状态。
4.在 Pod 终止之前,所有的继任者必须完全关闭。
StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。这种做法是不安全的,要强烈阻止。

更多的解释请参考 强制删除 StatefulSet Pod。

在上面的 Nginx 示例被创建后,会按照 web-0、web-1的顺序部署Pod。在 web-0 进入 Running 和 Ready 状态前不会部署 web-1。在 web-1 进入 Running 和 Ready 状态前不会部署 web-2。如果 web-1 已经处于 Running 和 Ready 状态,而 web-2 尚未部署,在此期间发生了 web-0 运行失败,那么 web-2 将不会被部署,要等到 web-0 部署完成并进入 Running 和 Ready 状态后,才会部署 web-2。如果用户想将示例中的 StatefulSet 收缩为 replicas=1,首先被终止的是 web-2。在 web-2 没有被完全停止和删除前,web-1 不会被终止。当 web-2 已被终止和删除、web-1 尚未被终止,如果在此期间发生 web-0 运行失败,那么就不会终止 web-1,必须等到 web-0 进入 Running 和 Ready 状态后才会终止 web-1。

5.Statefulset的资源清单yaml文件书写规范

1.Pod selector
你必须设置StatefulSet的.spec.selector字段,使之匹配其在.spec.template.Metadata.labels中设置的标签。在Kubernetes 1.8版本之前,被忽略.spec.selector字段会获得默认设置值。在 1.8和以后的版本中,未指定匹配的Pod 选择器将在创建 StatefulSet 期间导致验证错误。
2.Pod Identity(pod标识)
StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。该标识和Pod是绑定的,不管它被调度在哪个节点上。
3.有序索引
对于具有N个副本的StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号,从 0 到 N-1,该序号在StatefulSet 上是唯一的。
4.稳定的网络ID
1)StatefulSet 中的每个Pod根据StatefulSet的名称和Pod的序号派生出它的主机名。组合主机名的格式为$(StatefulSet 名称)-$(序号)。上例将会创建名称分别为 web-0、web-1的Pod。 StatefulSet 可以使用headless 服务控制它的Pod的网络域。管理域的这个服务的格式为: $(服务名称).$(命名空间).svc.cluster.local,其中cluster.local是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为:$(pod 名称).$(所属服务的 DNS 域名),其中所属服务由 StatefulSet的serviceName域来设定。
2)下面给出一些选择集群域、服务名、StatefulSet 名、及其怎样影响 StatefulSet 的 Pod 上的 DNS 名称的示例:

注意: 集群域会被设置为 cluster.local,除非有其他配置。

5.稳定的存储
1)Kubernetes 为每个 VolumeClaimTemplate 创建一个 PersistentVolume。在上面的 Nginx 示例中,每个 Pod 将会得到基于 StorageClass my-storage-class 提供的 1 Gib 的 PersistentVolume。如果没有声明 StorageClass,就会使用默认的 StorageClass。当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。
2)Pod 名称标签
3)当StatefulSet控制器创建 Pod 时,它会添加一个标签statefulset.kubernetes.io/pod-name,该标签设置为Pod名称。这个标签允许您给 StatefulSet 中的特定 Pod 绑定一个 Service。

6.Pod 管理策略

在 Kubernetes 1.7 及以后的版本中,StatefulSet 允许你不要求其排序保证,同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。 在 Kubernetes 1.7 及以后的版本中,StatefulSet 允许您放宽其排序保证,同时通过它的 .spec.podManagementPolicy 域保持其唯一性和身份保证。

7.OrderedReady Pod 管理

OrderedReady Pod 管理是 StatefulSet 的默认设置。它实现了上面描述的功能。

8.Parallel Pod 管理

Parallel Pod 管理让 StatefulSet 控制器并行的启动或终止所有的 Pod,启动或者终止其他 Pod 前,无需等待 Pod 进入 Running 和 ready 或者完全停止状态。

9.更新策略

在Kubernetes 1.7及以后的版本中,StatefulSet 的.spec.updateStrategy 字段让你可以配置和禁用掉自动滚动更新Pod的容器、标签、资源请求或限制、以及注解。

10.关于删除策略

OnDelete 更新策略实现了 1.6 及以前版本的历史遗留行为。当 StatefulSet 的 .spec.updateStrategy.type 设置为 OnDelete 时,它的控制器将不会自动更新 StatefulSet 中的 Pod。用户必须手动删除Pod以便让控制器创建新的 Pod,以此来对StatefulSet的 .spec.template 的变动作出反应。

11.滚动更新

RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。在没有声明 .spec.updateStrategy 时,RollingUpdate 是默认配置。 当 StatefulSet 的 .spec.updateStrategy.type 被设置为 RollingUpdate 时,StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。它会等到被更新的 Pod 进入 Running 和 Ready 状态,然后再更新其前身。

12.分区

# 打补丁方式修改pod partition序号大于或等于3的进行升级
kubectl patch sts web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":3}}}}'
Kubectl describe sts web  # 查看partition字段

kubectl patch sts web -p ‘{“spec”:{“template”:{“spec”:{“containers[0]”:{“image”:”Nginx:1.21”}}}}}’
containers[0]:一个pod中可能有多个容器,containers是一个容器列表,所以我们要指定修改哪一个容器的镜像版本。
或者:
kubectl set image sts/web Nginx=Nginx:1.21
kubectl get sts -o wide # 查看sts镜像版本是否被修改成功

# 查看web-3的pod的镜像是否改变
kubectl describe pod web-3

# 而web-0,1,2的镜像还是Nginx:1.20
kubectl describe pod web-2

@H_5_301@

这就实现了金丝雀发布,如果发现pod web-3在使用新镜像没有问题,将pod web-0,1,2的镜像全部升级到Nginx:1.21 
kubectl patch sts web -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'

通过声明 .spec.updateStrategy.rollingUpdate.partition 的方式,RollingUpdate 更新策略可以实现分区。如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时,所有序号大于等于该分区序号的 Pod 都会被更新。所有序号小于该分区序号的 Pod 都不会被更新,并且,即使他们被删除也会依据之前的版本进行重建。如果StatefulSet的 .spec.updateStrategy.rollingUpdate.partition 大于它的 .spec.replicas,对它的 .spec.template 的更新将不会传递到它的 Pod。 在大多数情况下,您不需要使用分区,但如果您希望进行阶段更新、执行金丝雀或执行分阶段展开,则这些分区会非常有用。

13.强制回滚

在默认 Pod 管理策略(OrderedReady) 时使用 滚动更新 ,可能进入需要人工干预才能修复的损坏状态。
如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如,由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。
在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。
恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样,StatefulSet 才会开始使用被还原的模板来重新创建 Pod。

以上还是用无状态应用模拟的sts pod,而在实际使用statefulSet管理有状态应用并没这么简单,比如管理redis集群,etcd集群:

14.statefulSet 控制器搭建Redis:

参考博客:https://www.cnblogs.com/kuku0223/p/10906003.html

15.statefulSet控制器搭建etcd集群:

Cat etcd-statefulSet.yaml
apiVersion: apps/v1
kind: StatefulSet
Metadata:
  name: etcd
  labels:
    app: etcd
spec:
  serviceName: etcd
  # changing replicas value will require a manual etcdctl member remove/add
  # command (remove before decreasing and add after increasing)
  replicas: 3
  selector: 
    matchLabels: 
      app: etcd-member
  template:
    Metadata:
      name: etcd
      labels:
        app: etcd-member
    spec:
      containers:
      - name: etcd
        image: elcolio/etcd 
        ports:
        - containerPort: 2379
          name: client
        - containerPort: 2380
          name: peer
        env:
        - name: CLUSTER_SIZE
          value: "3"
        - name: SET_NAME
          value: "etcd"
        volumeMounts:
        - name: data
          mountPath: /var/run/etcd
        command:
        - "/bin/sh"
        - "-ecx"
        - |
          IP=$(hostname -i)
          PEERS=""
          for i in $(seq 0 $((${CLUSTER_SIZE} - 1))); do
              PEERS="${PEERS}${PEERS:+,}${SET_NAME}-${i}=http://${SET_NAME}:2380"
          done
          # start etcd. If cluster is already initialized the `--initial-*` options will be ignored.
          exec etcd --name $(HOSTNAME) \
            --listen-peer-urls http://${IP}:2380 \
            --listen-client-urls http://$(IP):2379,http://127.0.0.1:2379 \
            --advertise-client-urls http://${HOSTNAME}.${SET_NAME}:2379 \
            --initial-advertise-peer-urls http://${HOSTNAME}.${SET_NAME}:2380 \
            --initial-cluster-token etcd-cluster-1 \
            --initial-cluster ${PEERS} \
            --initial-cluster-state new \
            --data-dir /var/run/etcd/default.etcd
      volumeClaimTemplates: 
      - Metadata:
          name: data
        spec:
          storageClassName: gluster-dynamic
          accessModes:
            - "ReadWriteOnce"
          resources:
            requests:
              storage: 1Gi

k8s 1.25学习4 - 使用Deployment、StatefulSet部署应用

k8s 1.25学习4 - 使用Deployment、StatefulSet部署应用

k8s集群中apiserver是无状态的,可以有多台同时工作
Controller-Manager、scheduler是有状态的,会选举出一个master节点工作

#查看主节点
kubectl get leases -n kube-system

CoreDNS:Service路由解析

#显示和操作IP路由表
route -n

kubectl run mynginx --image=nginx
kubectl exec -it mynginx -- sh
#nameserver指向dns的IP
cat /etc/resolv.conf
exit

kubectl get svc -A | grep dns
kubectl delete pod mynginx

Pod常用命令

每个Pod包含一个Pause容器
Pause容器是Pod的父容器,负责僵尸进程的回收管理,通过Pause容器使同一个Pod里的容器共享存储、网络、PID、IPC等

#查看k8s.io命名空间内的静态容器
ctr -n k8s.io container ls
ctr -n k8s.io task ls

https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/
Pod官方文档

使用文件创建Pod
#输出Pod的yaml文件,不会自动创建Pod
kubectl run nginx --image=nginx:1.15.12 -oyaml --dry-run=client > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx:1.15.12
    name: nginx

kubectl apply -f pod.yaml
查看yaml文件格式中的版本号
kubectl api-resources | grep pod
kubectl api-resources | grep deployment
覆盖镜像中entrypoint命令
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  containers:
  - image: nginx:1.15.12
    name: nginx
    command: ["sleep", "10"]  #entrypoint
	
	
#查看帮忙文档	
kubectl explain Pod.spec.containers	

#command覆盖镜像中entrypoint
#args覆盖镜像中CMD中的参数

Pod镜像拉取策略
imagePullPolicy: IfNotPresent # 可选,镜像拉取策略,IfNotPresent、Never、IfNotPresent

Pod重启策略
#默认Always #OnFailure:容器执行entrypoint命令后,以不为0的状态码终止,则自动重启该容器
restartPolicy: Always #Always、OnFailure、Never


Pod的三种探针

startupProbe:判断容器内的应用程序是否已经启动
livenessProbe:探测容器是否在运行;如果不满足健康条件,根据Pod中设置的restartPolicy(重启策略)来判断,Pod是否要进行重启操作
readinessProbe:探测容器内的程序是否健康,即判断容器是否为就绪(Ready)状态;不可用将从Service的Endpoints中移除

kubectl get svc -n kube-system
kubectl describe svc metrics-server -n kube-system
kubectl get pods -n kube-system -owide | grep metrics
kubectl get endpoints metrics-server -n kube-system

程序如果启动比较慢,需要使用startupProbe探针
只有等startupProbe检测通过了之后,才会开始检测livenessProbe、readinessProbe
应用启动时间大于30秒,就需要配置startupProbe,这样livenessProbe、readinessProbe的间隔检查时间就可以配置小一些了
vi nginx-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.15.12
    imagePullPolicy: IfNotPresent
    command:
    - sh
    - -c
    - sleep 30; nginx -g "daemon off;"
    startupProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 10 # 初始化时间
      timeoutSeconds: 2 # 超时时间
      periodSeconds: 5 # 检测间隔
      successThreshold: 1 # 检查成功为2次表示就绪
      failureThreshold: 5 # 检测失败1次表示未就绪
    readinessProbe: 
      httpGet:
        path: /index.html
        port: 80
        scheme: HTTP
      initialDelaySeconds: 10 # 初始化时间, 健康检查延迟执行时间
      timeoutSeconds: 2 # 超时时间
      periodSeconds: 5 # 检测间隔
      successThreshold: 1 # 检查成功为2次表示就绪
      failureThreshold: 2 # 检测失败1次表示未就绪
    livenessProbe: # 可选,健康检查
      exec: # 端口检测方式
        command:
        - sh
        - -c
        - pgrep nginx
      initialDelaySeconds: 10 # 初始化时间
      timeoutSeconds: 2 # 超时时间
      periodSeconds: 5 # 检测间隔
      successThreshold: 1 # 检查成功为 2 次表示就绪
      failureThreshold: 2 # 检测失败 1 次表示未就绪
    ports:
    - containerPort: 80
  restartPolicy: Never
kubectl apply -f nginx-pod.xml
kubectl delete -f nginx-pod.xml
preStop容器停止前执行指令

vi pod-preStop.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.15.12
    imagePullPolicy: IfNotPresent
    lifecycle:
      preStop:
       exec:
         command:
         - sh
         - -c
         - sleep 10
    ports: 
    - containerPort: 80 
  restartPolicy: Never
kubectl apply -f pod-preStop.yaml
kubectl delete -f pod-preStop.yaml

无状态应用管理Deployment

Replication Controller可确保Pod副本数达到期望值,即确保一个Pod或一组同类Pod总是可用
ReplicaSet是支持基于集合的标签选择器的下一代Replication Controller
它主要用作Deployment协调创建、删除和更新Pod,和Replication Controller唯一的区别是,ReplicaSet支持标签选择器

#生成deployment模板
kubectl create deploy nginx --image=nginx:1.15.12-alpine --replicas=3 -oyaml --dry-run=client > nginx-deploy.yaml
kubectl apply -f nginx-deploy.yaml
kubectl get deploy
kubectl get rs
kubectl get pod
kubectl delete pod nginx-b7599c689-qchww
kubectl get pod

查看整个Deployment创建的状态

kubectl rollout status deployment/nginx
kubectl get deploy
kubectl get rs -l app=nginx
kubectl get pods --show-label

更新Deployment

kubectl set image deployment nginx nginx=nginx:1.13 --record
kubectl rollout status deployment/nginx
kubectl describe deploy nginx

回滚Deployment

#查看历史版本
kubectl rollout history deployment nginx

#查看某一个版本的信息
kubectl rollout history deployment nginx --revision=3

#回滚到上一个版本
kubectl rollout undo deployment nginx

#回滚到指定版本
kubectl rollout undo deployment nginx --to-revision=3

扩容

kubectl scala deployment nginx --replicas=5 

暂停和恢复Deployment更新

kubectl rollout pause deployment nginx
#更新配置信息,但不重启pod
kubectl set image deployment nginx nginx=1.15.12-alpine --record
kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
#恢复pod,自动更新
kubectl rollout resume deployment nginx
kubectl get rs

Deployment更新策略
.spec.strategy.type==RollingUpdate,默认滚动更新
.spec.strategy.rollingUpdate.maxUnavailable,指定在回滚更新时最大不可用的Pod数量,默认为25%
.spec.strategy.rollingUpdate.maxSurge可以超过期望值的最大Pod数,默认为25%(一次启动多个副本,最大限制数量)


StatefulSet部署有状态应用

#StatefulSet服务域名格式:StatefulSetName-0.ServiceName.Namepace.svc.cluster.local
web-0.nginx.default.svc.cluster.local

StatefulSet需要创建一个Service对外提供服务
vi statefulset.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # by default is 1
  minReadySeconds: 10 # by default is 0
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.15.12-alpine
        ports:
        - containerPort: 80
          name: web

创建

kubectl apply -f statefulset.yaml
kubectl rollout status sts web
kubectl get pods
kubectl get sts

Pod创建时按顺序一个一个单独创建,缩容时按倒序一个一个接着关闭

kubectl exec -it web-2 --sh 
curl web-0.nginx.default.svc.cluster.local
nslookup web-0.nginx.default.svc.cluster.local

扩容

kubectl scale sts web --replicas=5
kubectl get pods
StatefulSet更新策略

RollingUpdate默认滚动更新

分段更新partition

  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate

partition默认值为0,表示更新后缀大于等于0的Pod;如果配置为3,则表示只更新后缀大于等于3的Pod,而小于3的Pod不会更新;可以用于灰度测试

删除sts

kubectl delete sts web
kubectl get sts
kubectl get pod 

#删除default命名空间中的所有pod
kubectl delete pod --all

守护进程集DaemonSet

每个节点上可以有一个守护进程集

kubectl get ds -n kube-system

#DaemonSet的yaml与Deployment类似,只是少了副本这一个参数
#删除replicas: 3,修改kind: DaemonSet
cp nginx-deploy.yaml nginx-ds.yaml
kubectl apply -f nginx-ds.yaml
kubectl get ds
kubectl get pods

查看node节点上的label

kubectl get node --show-labels
#node节点上增加label
kubectl label node k8s-node2 k8s-node3 disktype=ssd
kubectl get node --show-labels

指定节点部署Pod:.spec.template.spec.nodeSelector

#节点选择器
    nodeSelector:
      disktype: ssd
kubectl apply -f nginx-ds.yaml
kubectl get pods -owide


kubectl get ds -oyaml
kubectl get ds 
kubectl rollout status ds/nginx
kubectl rollout history daemonset nginx
kubectl rollout undo daemonset nginx
kubectl get controllerrevision
kubectl delete -f nginx-ds.yaml

HPA自动扩缩容

kubectl get apiservices | grep autoscaling
kbuectl create deployment hpa-nginx --image=nginx:1.15.12-alpine --dry-run=client -oyaml > hpa-nginx.yaml

1颗CPU是1000m
创建一个deploy,限制CPU资源为10m

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hpa-nginx
  name: hpa-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hpa-nginx
  template:
    metadata:
      labels:
        app: hpa-nginx
    spec:
      containers:
      - image: nginx:1.15.12-alpine
        name: nginx
        resources: 
          requests: 
            cpu: 10m

创建

kubectl apply -f hpa-nginx.yaml 
kubectl get deploy

#创建Service
kubectl expose deploy hpa-nginx --port=80
kubectl get svc
curl http://10.96.226.0
kubectl top pods

创建hpa

#CPU使用率超过10%时开始扩容
kubectl autoscale deploy hpa-nginx --cpu-percent=10 --min=1 --max=10
kubectl get hpa
kubectl get hpa -oyaml

压测

kubectl get svc
#增大访问压力
while true; do wget -q -O - http://10.96.226.0 > /dev/null; done
kubectl top pods
kubectl logs -f hpa-nginx-54c8954b44-j5spv
kubectl top pods
kubectl get hpa
kubectl delete -f hpa-nginx.yaml

Label标签选择器

Label可以对k8s的一些对象进行分组,用于区分同样的资源不同的分组
Selector可以根据资源的标签查询出精确的对象信息
一般不修改Pod的标签,常修改Node的标签

kubectl get pods --show-labels
kubectl get pods -l app=nginx
kubectl get nodes --show-labels
kubectl get nodes -l disktype=ssd
kubectl get svc -n kube-system --show-labels

给节点打标签

kubectl label node k8s-node2 region=sz
kubectl get node -l region=sz


#yaml使用nodeSelector选择部署在指定的节点上
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      nodeSelector:
        region: sz

获取label中disktype为nginx或者ssd的数据

kubectl get nodes -l ''disktype in (nginx, ssd)'' --show-labels

#匹配多个条件
kubectl get nodes -l ''region!=sz, disktype in (nginx, ssd)'' --show-labels

#匹配是否存在标签
kubectl get nodes -l region

修改标签

kubectl label node k8s-node2 region=sh --overwrite

#批量修改标签
kubectl label node -l region region=wh --overwrite

删除标签

kubectl label node k8s-node2 region-

k8s StatefulSet

k8s StatefulSet

StatefulSet

StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设计),其应用场景包括

  • 稳定的持久化存储,即 Pod 重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现
  • 稳定的网络标志,即 Pod 重新调度后其 PodName 和 HostName 不变,基于 Headless Service(即没有 Cluster IP 的 Service)来实现
  • 有序部署,有序扩展,即 Pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从 0 到 N-1,在下一个 Pod 运行之前所有之前的 Pod 必须都是 Running 和 Ready 状态),基于 init containers 来实现
  • 有序收缩,有序删除(即从 N-1 到 0)

从上面的应用场景可以发现,StatefulSet 由以下几个部分组成:

  • 用于定义网络标志(DNS domain)的 Headless Service
  • 用于创建 PersistentVolumes 的 volumeClaimTemplates
  • 定义具体应用的 StatefulSet

StatefulSet 中每个 Pod 的 DNS 格式为 statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中

  • serviceName 为 Headless Service 的名字
  • 0..N-1 为 Pod 所在的序号,从 0 开始到 N-1
  • statefulSetName 为 StatefulSet 的名字
  • namespace 为服务所在的 namespace,Headless Servic 和 StatefulSet 必须在相同的 namespace
  • .cluster.local 为 Cluster Domain,

简单示例

以一个简单的 nginx 服务 web.yaml 为例:

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: gcr.io/google_containers/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
$ kubectl create -f web.yaml
service "nginx" created
statefulset "web" created

# 查看创建的headless service和statefulset
$ kubectl get service nginx
NAME      CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx     None         <none>        80/TCP    1m
$ kubectl get statefulset web
NAME      DESIRED   CURRENT   AGE
web       2         2         2m

# 根据volumeClaimTemplates自动创建PVC(在GCE中会自动创建kubernetes.io/gce-pd类型的volume)
$ kubectl get pvc
NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
www-web-0   Bound     pvc-d064a004-d8d4-11e6-b521-42010a800002   1Gi        RWO           16s
www-web-1   Bound     pvc-d06a3946-d8d4-11e6-b521-42010a800002   1Gi        RWO           16s

# 查看创建的Pod,他们都是有序的
$ kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
web-0     1/1       Running   0          5m
web-1     1/1       Running   0          4m

# 使用nslookup查看这些Pod的DNS
$ kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
/ # nslookup web-0.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.244.2.10
/ # nslookup web-1.nginx
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.244.3.12
/ # nslookup web-0.nginx.default.svc.cluster.local
Server:    10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx.default.svc.cluster.local
Address 1: 10.244.2.10

还可以进行其他的操作

# 扩容
$ kubectl scale statefulset web --replicas=5

# 缩容
$ kubectl patch statefulset web -p ''{"spec":{"replicas":3}}''

# 镜像更新(目前还不支持直接更新image,需要patch来间接实现)
$ kubectl patch statefulset web --type=''json'' -p=''[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"gcr.io/google_containers/nginx-slim:0.7"}]''

# 删除StatefulSet和Headless Service
$ kubectl delete statefulset web
$ kubectl delete service nginx

# StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除
$ kubectl delete pvc www-web-0 www-web-1

zookeeper

另外一个更能说明 StatefulSet 强大功能的示例为 zookeeper.yaml。

---
apiVersion: v1
kind: Service
metadata:
  name: zk-headless
  labels:
    app: zk-headless
spec:
  ports:
  - port: 2888
    name: server
  - port: 3888
    name: leader-election
  clusterIP: None
  selector:
    app: zk
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: zk-config
data:
  ensemble: "zk-0;zk-1;zk-2"
  jvm.heap: "2G"
  tick: "2000"
  init: "10"
  sync: "5"
  client.cnxns: "60"
  snap.retain: "3"
  purge.interval: "1"
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: zk-budget
spec:
  selector:
    matchLabels:
      app: zk
  minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: zk
spec:
  serviceName: zk-headless
  replicas: 3
  template:
    metadata:
      labels:
        app: zk
      annotations:
        pod.alpha.kubernetes.io/initialized: "true"
        scheduler.alpha.kubernetes.io/affinity: >
            {
              "podAntiAffinity": {
                "requiredDuringSchedulingRequiredDuringExecution": [{
                  "labelSelector": {
                    "matchExpressions": [{
                      "key": "app",
                      "operator": "In",
                      "values": ["zk-headless"]
                    }]
                  },
                  "topologyKey": "kubernetes.io/hostname"
                }]
              }
            }
    spec:
      containers:
      - name: k8szk
        imagePullPolicy: Always
        image: gcr.io/google_samples/k8szk:v1
        resources:
          requests:
            memory: "4Gi"
            cpu: "1"
        ports:
        - containerPort: 2181
          name: client
        - containerPort: 2888
          name: server
        - containerPort: 3888
          name: leader-election
        env:
        - name : ZK_ENSEMBLE
          valueFrom:
            configMapKeyRef:
              name: zk-config
              key: ensemble
        - name : ZK_HEAP_SIZE
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: jvm.heap
        - name : ZK_TICK_TIME
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: tick
        - name : ZK_INIT_LIMIT
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: init
        - name : ZK_SYNC_LIMIT
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: tick
        - name : ZK_MAX_CLIENT_CNXNS
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: client.cnxns
        - name: ZK_SNAP_RETAIN_COUNT
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: snap.retain
        - name: ZK_PURGE_INTERVAL
          valueFrom:
            configMapKeyRef:
                name: zk-config
                key: purge.interval
        - name: ZK_CLIENT_PORT
          value: "2181"
        - name: ZK_SERVER_PORT
          value: "2888"
        - name: ZK_ELECTION_PORT
          value: "3888"
        command:
        - sh
        - -c
        - zkGenConfig.sh && zkServer.sh start-foreground
        readinessProbe:
          exec:
            command:
            - "zkOk.sh"
          initialDelaySeconds: 15
          timeoutSeconds: 5
        livenessProbe:
          exec:
            command:
            - "zkOk.sh"
          initialDelaySeconds: 15
          timeoutSeconds: 5
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/zookeeper
      securityContext:
        runAsUser: 1000
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
        volume.alpha.kubernetes.io/storage-class: anything
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi
kubectl create -f zookeeper.yaml

详细的使用说明见 zookeeper stateful application。

StatefulSet 注意事项

  1. 还在 beta 状态,需要 kubernetes v1.5 版本以上才支持
  2. 所有 Pod 的 Volume 必须使用 PersistentVolume 或者是管理员事先创建好
  3. 为了保证数据安全,删除 StatefulSet 时不会删除 Volume
  4. StatefulSet 需要一个 Headless Service 来定义 DNS domain,需要在 StatefulSet 之前创建好
  5. 目前 StatefulSet 还没有 feature complete,比如更新操作还需要手动 patch。

更多可以参考 Kubernetes 文档。

原文:https://github.com/rootsongjc/kubernetes-handbook/blob/master/architecture/statefulset.md

k8s StatefulSet控制器

k8s StatefulSet控制器

StatefulSet控制器

StatefulSet 是用来管理有状态应用的控制器。
参考: https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/

@H_301_8@无状态应用与有状态应用

无状态应用: 如Nginx

  • 请求本身包含了响应端为响应这一请求所需的全部信息。每一个请求都像首次执行一样,不会依赖之前的数据进行响应。
  • 不需要持久化的数据
  • 无状态应用的多个实例之间互不依赖,可以无序的部署、删除或伸缩

有状态应用: 如MysqL

  • 前后请求有关联与依赖
  • 需要持久化的数据
  • 有状态应用的多个实例之间有依赖,不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
@H_301_8@StatefulSet的特点
  • 稳定的、唯一的网络标识符。 (通过headless服务实现)
  • 稳定的、持久的存储。 (通过PV,PVC,storageclass实现)
  • 有序的、优雅的部署和缩放。
  • 有序的、自动的滚动更新。
@H_301_8@StatefulSet的YAML组成

需要三个组成部分:

  1. headless service: 实现稳定,唯一的网络标识
  2. statefulset类型资源: 写法和deployment几乎一致,就是类型不一样
  3. volumeClaimTemplate : 指定存储卷

Nginx+statefulset+nfs案例

@H_301_8@一、创建StatefulSet应用

参考: https://kubernetes.io/zh/docs/tutorials/stateful-application/basic-stateful-set/

温馨提示:做此实验前要先创建好pv,pvc等,可参考博客 https://blog.51cto.com/u_13760351/2639942

1.创建statelfulset应用

(来调用名为managed-nfs-storage的storageclass,以实现动态供给)

vim Nginx-storageclass-nfs.yml

apiVersion: v1
kind: Service
Metadata:
  name: Nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None                                  # 无头服务
  selector:
    app: Nginx
---
apiVersion: apps/v1
kind: StatefulSet
Metadata:
  name: web                                         # statefulset的名称
spec:
  serviceName: "Nginx"                              # 服务名与上面的无头服务名要一致
  replicas: 3                                       # 3个副本
  selector:
    matchLabels:
      app: Nginx
  template:
    Metadata:
      labels:
        app: Nginx
    spec:
      containers:
      - name: Nginx
        image: Nginx:1.15-alpine
        volumeMounts:
        - name: www
          mountPath: /usr/share/Nginx/html

  volumeClaimTemplates:
  - Metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"       # 与前面定义的storageclass名称对应
      resources:
        requests:
          storage: 1Gi

kubectl apply -f Nginx-storageclass-nfs.yml
kubectl get statefulsets # 可以简写成sts

k8s  StatefulSet控制器

@H_301_8@二、验证pod、pv、pvc

kubectl get pods |grep web

k8s  StatefulSet控制器

自动产生了3个pv

kubectl get pv

k8s  StatefulSet控制器

自动产生了3个PVC

kubectl get pvc |grep web

k8s  StatefulSet控制器

@H_301_8@三、验证nfs服务目录

在nfs服务器(这里为hostos)的共享目录中发现自动产生了3个子目录

[root@hostos ~]# ls /data/

k8s  StatefulSet控制器

3个子目录默认都为空目录

[root@hostos ~]# tree /data/

k8s  StatefulSet控制器

@H_301_8@四、验证存储持久性

1.在3个pod中其中一个创建一个主页文件

kubectl exec -it web-0 -- /bin/sh
/ # echo "haha" > /usr/share/Nginx/html/index.html
/ # exit

在nfs服务器上发现文件被创建到了对应的目录中

[root@hostos ~]# tree /data/

k8s  StatefulSet控制器

[root@hostos ~]# cat /data/default-www-web-0-pvc-2436b20d-1be3-4c2e-87a9-5533e5c5e2c6/index.html

k8s  StatefulSet控制器

2.删除web-0这个pod,再验证

kubectl delete pod web-0
kubectl get pods |grep web #因为控制器的原因,再拉起web-0这个pod

k8s  StatefulSet控制器

kubectl exec -it web-0 -- cat /usr/share/Nginx/html/index.html

k8s  StatefulSet控制器

新拉起的pod仍然是相同的存储数据

[root@hostos ~]# cat /data/nfs/default-www-web-0-pvc-2436b20d-1be3-4c2e-87a9-5533e5c5e2c6/index.html

k8s  StatefulSet控制器

nfs服务器上的数据还在

3.结论: 说明数据可持久化

@H_301_8@五、验证pod唯一名称

回顾域名格式:

service: <service name>.<namespace name>.svc.cluster.local.
pod: <PodName>.<service name>.<namespace name>.svc.cluster.local.

可以看到在 web-0这个pod中,nslookup查询service的域名,直接解析成了3个pod的域名

kubectl exec -it web-0 -- /bin/sh
/ # nslookup Nginx.default.svc.cluster.local.

k8s  StatefulSet控制器

k8s  StatefulSet控制器

ping这三个pod的域名都可以ping通

/ # ping web-0.Nginx.default.svc.cluster.local.

k8s  StatefulSet控制器

/ # ping web-1.Nginx.default.svc.cluster.local

k8s  StatefulSet控制器

/ # ping ping web-2.Nginx.default.svc.cluster.local

k8s  StatefulSet控制器

补充: 当pod被删除后,重新拉起来,pod-IP可能会变,但上面的pod域名仍然可以ping通

k8s  StatefulSet控制器

k8s  StatefulSet控制器

@H_301_8@六、验证statefulset的伸缩

1.扩容

kubectl scale sts web --replicas=4
kubectl get pods

k8s  StatefulSet控制器

有序地扩展了一个pod,名称为web-3

2.裁剪

kubectl scale sts web --replicas=1
kubectl get pods |grep web

k8s  StatefulSet控制器

先裁剪web-3,再裁剪web-2,最后裁剪web-1,只留下web-0

MysqL-statefulset-nfs案例

@H_301_8@一、编写statefulset

vim statefulset-MysqL-nfs.yaml

apiVersion: v1
kind: Service
Metadata:
  name: MysqL-svc
spec:
  clusterIP: None                   # 无头服务
  ports:
  - port: 3306
    protocol: TCP
    targetPort: 3306
  selector:
    app: MysqL
---
apiVersion: apps/v1
kind: StatefulSet
Metadata:
  name: MysqL
spec:
  serviceName: MysqL-svc            # 与上面服务名一致
  replicas: 1                       # 副本数为1就OK,这里不做MysqL集群
  selector:
    matchLabels:
      app: MysqL
  template:
    Metadata:
      labels:
        app: MysqL
    spec:
      containers:
      - name: c1
        image: MysqL:5.7
        env:
        - name: MysqL_ROOT_PASSWORD     # MysqL5.7的镜像必须要设置一下MysqL的root密码
          value: "123456"
        - name: MysqL_DATABASE          # 给它建一个库名为daniel,用于验证
          value: daniel
        volumeMounts:
        - name: MysqL-data
          mountPath: /var/lib/MysqL
  volumeClaimTemplates:
  - Metadata:
      name: MysqL-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-nfs-storage"          
      resources:
        requests:
          storage: 3Gi
@H_301_8@二、应用YAML

kubectl apply -f statefulset-MysqL-nfs.yaml

@H_301_8@三、验证资源

kubectl get pods |grep MysqL

k8s  StatefulSet控制器

kubectl get sts |grep MysqL

k8s  StatefulSet控制器

kubectl get pv |grep MysqL

k8s  StatefulSet控制器

kubectl get pvc |grep MysqL

k8s  StatefulSet控制器

@H_301_8@四、验证MysqL

1.进入容器

kubectl exec -it MysqL-0 -- /bin/bash

root@MysqL-0:/# MysqL -p123456
MysqL> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| daniel             |                  # 这里daneil库就是帮我们创建的
| MysqL              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)
MysqL> exit

k8s  StatefulSet控制器

2.验证nfs上的数据

[root@hostos ~]# ls /data/default-MysqL-data-MysqL-0-pvc-f936d526-f473-4b88-a0e6-b94a19b95fe6

k8s  StatefulSet控制器

删除MysqL-0这个pod,会帮我们再次启动,并且数据还是用原来的数据

k8s  StatefulSet控制器

今天的关于k8s中controller部署有状态应用StatefulSetk8s部署有状态服务的分享已经结束,谢谢您的关注,如果想了解更多关于07-K8S之Statefulset控制器、k8s 1.25学习4 - 使用Deployment、StatefulSet部署应用、k8s StatefulSet、k8s StatefulSet控制器的相关知识,请在本站进行查询。

本文标签: