Kubernetes的有状态应用:基础

文章目录

  • 环境
  • 创建StatefulSet
    • 顺序创建pod
  • StatefulSet中的pod
    • 检查pod的顺序索引
    • 使用稳定的网络身份标识
      • 发现StatefulSet中特定的pod
    • 写入稳定的存储
  • 伸缩StatefulSet
    • 扩容
    • 缩容
    • 顺序终止pod
  • 更新StatefulSet
    • 滚动更新
      • 分段更新
      • 金丝雀发布
      • 分阶段发布
    • OnDelete
  • 删除 StatefulSet
    • 非级联删除
    • 级联删除
  • Pod 管理策略
    • Parallel pod管理策略
  • 清理
  • 参考

环境

  • RHEL 9.3
  • Docker Community 24.0.7
  • minikube v1.32.0

创建StatefulSet

在一个终端窗口中,持续监视pod:

kubectl get pods --watch -l app=nginx

创建文件 application/web/web.yaml 如下:

apiVersion: v1
kind: Service
metadata:name: nginxlabels:app: nginx
spec:ports:- port: 80name: webclusterIP: Noneselector:app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: web
spec:serviceName: "nginx"replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx# image: registry.k8s.io/nginx-slim:0.8image: kaiding1/nginx-slim:0.8ports:- containerPort: 80name: webvolumeMounts:- name: wwwmountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: wwwspec:accessModes: [ "ReadWriteOnce" ]resources:requests:storage: 1Gi

注:因为访问不了 registry.k8s.io ,所以事先把image pull下来,并push到了可访问的位置。

$ kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created
$ kubectl get service nginx
NAME    TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   None         <none>        80/TCP    2m30s
$ kubectl get statefulset web
NAME   READY   AGE
web    2/2     3m10s

顺序创建pod

如果StatefulSet有 n 个副本,则pod在部署时是按 {0..n-1} 的顺序创建的。

回到监视窗口,如下:

$ kubectl get pods --watch -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          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          1s

注意:pod web-0 处于 Running 状态并 READY 后,pod web-1 才会启动。

StatefulSet中的pod

StatefulSet中的每个pod都有一个唯一的顺序索引和稳定的网络身份标识。

检查pod的顺序索引

$ kubectl get pods -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          7m28s
web-1   1/1     Running   0          7m26s

StatefulSet中的pod拥有一个粘性的、唯一的身份标志。该标志基于StatefulSet控制器分配给每个pod 的唯一顺序索引。Pod的命名格式为 <statefulset name>-<ordinal index> 。本例中, web StatefulSet 拥有两个副本,所以创建了两个pod:web-0web-1

使用稳定的网络身份标识

每个pod都有一个基于顺序索引的稳定的hostname:

$ for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
web-0
web-1

使用 kubectl run 运行一个提供 nslookup 命令的容器,该命令来自于 dnsutils 包。通过 nslookup pod的hostname,可以检查它们在集群内部的DNS地址:

$ kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
If you don't see a command prompt, try pressing enter.
/ # 
/ # nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      web-0.nginx
Address 1: 10.244.0.144 web-0.nginx.default.svc.cluster.local
/ # nslookup web-1.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      web-1.nginx
Address 1: 10.244.0.145 web-1.nginx.default.svc.cluster.local

输入 exit 退出。

Headless service的CNAME指向SRV记录(每个 RunningReady 的pod对应一条记录)。SRV记录指向一个包含pod IP地址的记录条目。

删除StatefulSet中的pod:

$ kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

回到监视窗口,如下:

$ kubectl get pods --watch -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          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          1s
web-0   1/1     Terminating         0          33m
web-1   1/1     Terminating         0          33m
web-0   0/1     Terminating         0          33m
web-1   0/1     Terminating         0          33m
web-0   0/1     Terminating         0          33m
web-0   0/1     Terminating         0          33m
web-0   0/1     Terminating         0          33m
web-1   0/1     Terminating         0          33m
web-0   0/1     Pending             0          0s
web-0   0/1     Pending             0          0s
web-1   0/1     Terminating         0          33m
web-1   0/1     Terminating         0          33m
web-0   0/1     ContainerCreating   0          0s
web-0   1/1     Running             0          1s
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          1s
$ kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm
If you don't see a command prompt, try pressing enter.
/ # 
/ # nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      web-0.nginx
Address 1: 10.244.0.147 web-0.nginx.default.svc.cluster.local
/ # nslookup web-1.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      web-1.nginx
Address 1: 10.244.0.148 web-1.nginx.default.svc.cluster.local

输入 exit 退出。

Pod 的序号、主机名、SRV记录和记录名称没有改变,但pod关联的IP地址可能发生了改变。所以在其它应用中,不要通过IP地址来连接StatefulSet中的pod。

发现StatefulSet中特定的pod

如果要查找并连接StatefulSet的活动成员,需要查询headless service的CNAME( nginx.default.svc.cluster.local )。和CNAME相关联的SRV记录只包含StatefulSet中处于 RunningReady 状态的pod。

如果应用已经实现了用于测试是否已存活(liveness)和就绪(readiness)的连接逻辑,你可以使用pod的SRV记录( web-0.nginx.default.svc.cluster.localweb-1.nginx.default.svc.cluster.local )。因为它们是稳定的,当pod状态变为 RunningReady 时,应用就能够发现其地址。

写入稳定的存储

获取 web-0web-1 的PVC(PersistentVolumeClaims):

$ kubectl get pvc -l app=nginx
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-71418599-e0d4-4c45-9f3b-f56ebd992509   1Gi        RWO            standard       75m
www-web-1   Bound    pvc-c6741fad-942a-434a-b2a7-05efe540ca1e   1Gi        RWO            standard       68m

StatefulSet 控制器创建了两个绑定到PV(PersistentVolume)的PVC。

本例中使用的集群配置为动态provision PV,因此PV都是自动创建和绑定的。

NginX web服务器默认会加载 /usr/share/nginx/html/index.html 。StatefulSet spec 中的 volumeMounts 字段会确保 /usr/share/nginx/html 目录由一个PV支持。

将pod的hostname写入它们的 index.html 文件,并验证 NginX web服务器使用该hostname提供服务:

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 -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

注:如果遇到了 403 Forbidden

for i in 0 1; do kubectl exec web-$i -- chmod 755 /usr/share/nginx/html; done

在监视窗口,重新监视:

kubectl get pod --watch -l app=nginx

删除pod:

$ kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

回到监视窗口,如下:

$ kubectl get pods --watch -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          33m
web-1   1/1     Running   0          33m
web-0   1/1     Terminating   0          33m
web-1   1/1     Terminating   0          33m
web-0   0/1     Terminating   0          33m
web-1   0/1     Terminating   0          33m
web-0   0/1     Terminating   0          33m
web-0   0/1     Terminating   0          33m
web-0   0/1     Terminating   0          33m
web-1   0/1     Terminating   0          33m
web-0   0/1     Pending       0          0s
web-0   0/1     Pending       0          0s
web-1   0/1     Terminating   0          33m
web-1   0/1     Terminating   0          33m
web-0   0/1     ContainerCreating   0          0s
web-0   1/1     Running             0          1s
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          1s

验证web服务器会继续通过hostname提供服务:

$ for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

虽然 web-0web-1 被重新调度了,但它们仍然继续监听hostname,因为和PVC相关联的PV被重新mount到 volumeMount 。无论 web-0web-1 被调度到了哪个node,它们的PV将会被mount到合适的mount point。

伸缩StatefulSet

可通过 kubectl scale 或者 kubectl patch 来伸缩StatefulSet。

(注:原文写的是“scale up/down”(纵向伸缩),我觉得其实应该是“scale out/in”(横向伸缩)。)

扩容

在监视窗口,重新监视:

kubectl get pods --watch -l app=nginx
kubectl scale sts web --replicas=5

回到监视窗口,如下:

$ kubectl get pods --watch -l app=nginx
NAME    READY   STATUS    RESTARTS       AGE
web-0   1/1     Running   1 (110s ago)   153m
web-1   1/1     Running   1 (111s ago)   153m
web-2   0/1     Pending   0              0s
web-2   0/1     Pending   0              0s
web-2   0/1     Pending   0              23s
web-2   0/1     ContainerCreating   0              23s
web-2   1/1     Running             0              24s
web-3   0/1     Pending             0              0s
web-3   0/1     Pending             0              0s
web-3   0/1     Pending             0              2s
web-3   0/1     ContainerCreating   0              2s
web-3   1/1     Running             0              3s
web-4   0/1     Pending             0              0s
web-4   0/1     Pending             0              0s
web-4   0/1     Pending             0              2s
web-4   0/1     ContainerCreating   0              2s
web-4   1/1     Running             0              3s

StatefulSet按顺序索引串行创建pod,前一个pod变为 RunningReady 后才会启动下一个pod。

缩容

在监视窗口,重新监视:

kubectl get pods --watch -l app=nginx
kubectl patch sts web -p '{"spec":{"replicas":3}}'

回到监视窗口,如下:

$ kubectl get pods --watch -l app=nginx
NAME    READY   STATUS    RESTARTS        AGE
web-0   1/1     Running   1 (8m25s ago)   160m
web-1   1/1     Running   1 (8m26s ago)   160m
web-2   1/1     Running   0               6m23s
web-3   1/1     Running   0               5m59s
web-4   1/1     Running   0               5m56s
web-4   1/1     Terminating   0               6m2s
web-4   0/1     Terminating   0               6m2s
web-4   0/1     Terminating   0               6m3s
web-4   0/1     Terminating   0               6m3s
web-4   0/1     Terminating   0               6m3s
web-3   1/1     Terminating   0               6m6s
web-3   0/1     Terminating   0               6m6s
web-3   0/1     Terminating   0               6m7s
web-3   0/1     Terminating   0               6m7s
web-3   0/1     Terminating   0               6m7s

顺序终止pod

控制器会按照pod顺序索引的相反顺序,依次删除pod。在完全删除一个pod之后,才会删除下一个pod。

获取StatefulSet的PVC:

$ kubectl get pvc -l app=nginx
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-71418599-e0d4-4c45-9f3b-f56ebd992509   1Gi        RWO            standard       4h9m
www-web-1   Bound    pvc-c6741fad-942a-434a-b2a7-05efe540ca1e   1Gi        RWO            standard       4h2m
www-web-2   Bound    pvc-3164d5af-59b1-4984-ad2a-8a54ae2f0643   1Gi        RWO            standard       10m
www-web-3   Bound    pvc-5e5253e5-290c-4bd1-9727-c25a751e5d64   1Gi        RWO            standard       10m
www-web-4   Bound    pvc-797b39a1-a55c-47c4-92ab-6e0e2ff567ee   1Gi        RWO            standard       10m

可见,仍然有五个PVC和五个PV。当删除StatefulSet的pod时,mount的PV不会被删除。当StatefulSet缩容导致pod被删除时,也是一样。

更新StatefulSet

StatefulSet控制器支持自动更新。其策略由StatefulSet API对象的 spec.updateStrategy 字段决定。该特性可用来更新容器image、资源请求和/或限制、label和注解。

有两种更新策略:

  • RollingUpdate (默认)
  • OnDelete

滚动更新

RollingUpdate 更新策略会更新StatefulSet中的所有pod,采用与顺序索引相反的顺序,同时遵循对StatefulSet的保证(guarantee)。

对 web StatefulSet 应用 Patch 操作来应用 RollingUpdate 更新策略:

在监视窗口,重新监视:

kubectl get pods --watch -l app=nginx
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"docker.io/kaiding1/nginx-slim:0.7"}]'

注:原文是 gcr.io/google_containers/nginx-slim:0.8 ,这里有两个问题:

  • gcr.io 无法访问,所以事先要把image pull下来,并push到可访问的位置。
  • gcr.io/google_containers/nginx-slim:0.8registry.k8s.io/nginx-slim:0.8 是相同的,所以并不会触发patch,为了有差异,改为 0.7

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          3m59s
web-1   1/1     Running   0          4m1s
web-2   1/1     Running   0          4m3s
web-2   1/1     Terminating   0          15m
web-2   0/1     Terminating   0          15m
web-2   0/1     Terminating   0          15m
web-2   0/1     Terminating   0          15m
web-2   0/1     Pending       0          0s
web-2   0/1     Pending       0          0s
web-2   0/1     ContainerCreating   0          0s
web-2   0/1     ErrImagePull        0          4s
web-2   0/1     ImagePullBackOff    0          18s
web-2   0/1     ErrImagePull        0          34s
web-2   0/1     ImagePullBackOff    0          45s
web-2   0/1     ErrImagePull        0          61s
web-2   0/1     ImagePullBackOff    0          72s
web-2   0/1     ErrImagePull        0          113s
web-2   0/1     ImagePullBackOff    0          2m4s
web-2   0/1     ErrImagePull        0          3m29s
web-2   0/1     ImagePullBackOff    0          3m43s
web-2   0/1     ErrImagePull        0          6m23s
web-2   0/1     ImagePullBackOff    0          6m35s
web-2   0/1     ErrImagePull        0          11m
web-2   0/1     ImagePullBackOff    0          11m
web-2   1/1     Running             0          16m
web-1   1/1     Terminating         0          31m
web-1   0/1     Terminating         0          31m
web-1   0/1     Terminating         0          31m
web-1   0/1     Terminating         0          31m
web-1   0/1     Terminating         0          31m
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          1s
web-0   1/1     Terminating         0          31m
web-0   0/1     Terminating         0          31m
web-0   0/1     Terminating         0          31m
web-0   0/1     Terminating         0          31m
web-0   0/1     Terminating         0          31m
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          1s

可见,StatefulSet里的pod采用和序号相反的顺序更新。StatefulSet控制器终止每个pod,并等待它们变成 RunningReady ,然后再更新下一个pod。注意,StatefulSet控制器仍然会恢复在更新过程中发生故障的pod,恢复为当前版本。

注:从监视的输出结果,可见出问题时,watch的输出间隔大概是上一次的1.5倍到2倍时间。

接收到更新请求的pod将会被恢复为更新的版本,而没有收到更新请求的pod会被恢复为之前的版本。这样,即使出现间歇故障,控制器会尝试继续使应用保持健康以及更新的一致性。

查看pod:

$ for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
docker.io/kaiding1/nginx-slim:0.7
docker.io/kaiding1/nginx-slim:0.7
docker.io/kaiding1/nginx-slim:0.7

查看更新:

$ kubectl rollout status sts/web
partitioned roll out complete: 3 new pods have been updated...

分段更新

对于使用 RollingUpdate 策略的StatefulSet,可通过 .spec.updateStrategy.rollingUpdate.partition 把更新分隔为多个 partition 。具体参见 https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions

可以使用 .spec.updateStrategy.rollingUpdatepartition 字段来分段更新StatefulSet。 这样,使得StatefulSet中的pod不变的同时,改变StatefulSet的pod模板。然后,就可以触发准备好的升级。

首先,patch web StatefulSet,为 updateStrategy 字段添加partition:

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
kubectl patch statefulset web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"docker.io/kaiding1/nginx-slim:0.8"}]'
kubectl delete pod web-2

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS        AGE
web-0   1/1     Running   1 (4m53s ago)   5h19m
web-1   1/1     Running   1 (4m53s ago)   5h19m
web-2   1/1     Running   1 (4m53s ago)   5h36m
web-2   1/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Pending       0               0s
web-2   0/1     Pending       0               0s
web-2   0/1     ContainerCreating   0               0s
web-2   1/1     Running             0               1s
$ kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
docker.io/kaiding1/nginx-slim:0.7

注意:虽然更新策略是 RollingUpdate ,StatefulSet还是会使用原先的容器恢复pod。这是因为pod序号比 updateStrategy 指定的 partition 小。

金丝雀发布

注:关于金丝雀部署,参见 https://glossary.cncf.io/canary-deployment

金丝雀部署是一种部署策略,开始时有两个环境:一个有实时流量,另一个包含没有实时流量的更新代码。 流量逐渐从应用程序的原始版本转移到更新版本。 它可以从移动 1% 的实时流量开始,然后是 10%,25%,以此类推,直到所有流量都通过更新的版本运行。 企业可以在生产中测试新版本的软件,获得反馈,诊断错误,并在必要时快速回滚到稳定版本。

“金丝雀” 一词是指 “煤矿中的金丝雀” 的做法,即把金丝雀带入煤矿以保证矿工的安全。 如果出现无味的有害气体,鸟就会死亡,而矿工们知道他们必须迅速撤离。 同样,如果更新后的代码出了问题,现场交通就会被 “疏散” 回原来的版本。

可以通过减少 partition 来进行金丝雀发布,以测试修改的模板。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'

Control plane会触发 web-2 的替换(先delete再create)。

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS        AGE
web-0   1/1     Running   1 (4m53s ago)   5h19m
web-1   1/1     Running   1 (4m53s ago)   5h19m
web-2   1/1     Running   1 (4m53s ago)   5h36m
web-2   1/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Pending       0               0s
web-2   0/1     Pending       0               0s
web-2   0/1     ContainerCreating   0               0s
web-2   1/1     Running             0               1s
web-2   1/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Pending             0               0s
web-2   0/1     Pending             0               0s
web-2   0/1     ContainerCreating   0               0s
web-2   1/1     Running             0               1s
$ kubectl get pod web-2 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
docker.io/kaiding1/nginx-slim:0.8

当改变 partition 时,StatefulSet会自动更新 web-2 pod,这是因为pod的序号大于等于 partition

$ kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
docker.io/kaiding1/nginx-slim:0.7

删除 web-1 pod:

kubectl delete pod web-1

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS        AGE
web-0   1/1     Running   1 (4m53s ago)   5h19m
web-1   1/1     Running   1 (4m53s ago)   5h19m
......
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
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               1s
$ kubectl get pod web-1 --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'
docker.io/kaiding1/nginx-slim:0.7

web-1 被恢复为初始配置,因为pod序号小于分区。当指定了分区时,如果更新了StatefulSet的 .spec.template ,则所有序号大于等于分区的pod都将被更新。如果序号小于分区的pod被删除或者终止,它将被恢复为初始配置。

分阶段发布

与金丝雀发布的方法类似,可以执行分阶段发布(例如:线性的、几何的、或者指数的发布)。 要执行分阶段发布,要把 partition 设置为希望控制器暂停更新的序号。

kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS        AGE
web-0   1/1     Running   1 (4m53s ago)   5h19m
web-1   1/1     Running   1 (4m53s ago)   5h19m
web-2   1/1     Running   1 (4m53s ago)   5h36m
web-2   1/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m28s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Terminating   1 (6m29s ago)   5h38m
web-2   0/1     Pending       0               0s
web-2   0/1     Pending       0               0s
web-2   0/1     ContainerCreating   0               0s
web-2   1/1     Running             0               1s
web-2   1/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Terminating         0               14m
web-2   0/1     Pending             0               0s
web-2   0/1     Pending             0               0s
web-2   0/1     ContainerCreating   0               0s
web-2   1/1     Running             0               1s
web-1   1/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
web-1   0/1     Terminating         1 (28m ago)     5h42m
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               1s
web-1   1/1     Terminating         0               10m
web-1   0/1     Terminating         0               10m
web-1   0/1     Terminating         0               10m
web-1   0/1     Terminating         0               10m
web-1   0/1     Terminating         0               10m
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               1s
web-0   1/1     Terminating         1 (38m ago)     5h53m
web-0   0/1     Terminating         1 (38m ago)     5h53m
web-0   0/1     Terminating         1 (38m ago)     5h53m
web-0   0/1     Terminating         1 (38m ago)     5h53m
web-0   0/1     Terminating         1 (38m ago)     5h53m
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               1s
$ for p in 0 1 2; do kubectl get pod "web-$p" --template '{{range $i, $c := .spec.containers}}{{$c.image}}{{end}}'; echo; done
docker.io/kaiding1/nginx-slim:0.8
docker.io/kaiding1/nginx-slim:0.8
docker.io/kaiding1/nginx-slim:0.8

partition 设置为 0 ,则允许StatefulSet继续更新流程。

OnDelete

$ kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}'
The StatefulSet "web" is invalid: spec.updateStrategy.rollingUpdate: Invalid value: apps.RollingUpdateStatefulSetStrategy{Partition:0, MaxUnavailable:(*intstr.IntOrString)(nil)}: only allowed for updateStrategy 'RollingUpdate'

报错了,应该是哪里有冲突。

那就直接 kubectl edit 好了:

kubectl edit statefulset web

找到 updateStrategy ,修改如下:

  updateStrategy:type: OnDelete

对于 OnDelete 更新策略,当StatefulSet的 .spec.template 字段有修改时,StatefulSet控制器不会自动更新pod。需要自己处理更新——要么采取手工方式,要么使用其它自动化手段。

删除 StatefulSet

StatefulSet支持级联和非级联删除。对于非级联删除,当StatefulSet被删除时,其pod不会被删除。对于级联删除,StatefulSet和它的pod都会被删除。

非级联删除

在监视窗口,重新监视:

kubectl get pods --watch -l app=nginx
kubectl delete statefulset web --cascade=orphan

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS      AGE
web-0   1/1     Running   1 (95s ago)   11h
web-1   1/1     Running   1 (95s ago)   11h
web-2   1/1     Running   1 (95s ago)   12h
web-2   1/1     Running   1 (12m ago)   12h
web-1   1/1     Running   1 (12m ago)   12h
web-0   1/1     Running   1 (12m ago)   12h

可见, web 虽然被删除了,其pod仍然处于 RunningReady 状态。

删除 web-0

kubectl delete pod web-0

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS      AGE
web-0   1/1     Running   1 (95s ago)   11h
web-1   1/1     Running   1 (95s ago)   11h
web-2   1/1     Running   1 (95s ago)   12h
web-2   1/1     Running   1 (12m ago)   12h
web-1   1/1     Running   1 (12m ago)   12h
web-0   1/1     Running   1 (12m ago)   12h
web-0   1/1     Terminating   1 (14m ago)   12h
web-0   0/1     Terminating   1 (14m ago)   12h
web-0   0/1     Terminating   1 (14m ago)   12h
web-0   0/1     Terminating   1 (14m ago)   12h
web-0   0/1     Terminating   1 (14m ago)   12h

可见, web-0 不会再被重建。

在监视窗口,重新监视:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS      AGE
web-1   1/1     Running   1 (16m ago)   12h
web-2   1/1     Running   1 (16m ago)   12h

重新创建 web

$ kubectl apply -f web.yaml
service/nginx unchanged
statefulset.apps/web created

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS      AGE
web-1   1/1     Running   1 (16m ago)   12h
web-2   1/1     Running   1 (16m ago)   12h
web-1   1/1     Running   1 (18m ago)   12h
web-2   1/1     Running   1 (18m ago)   12h
web-0   0/1     Pending   0             0s
web-0   0/1     Pending   0             1s
web-0   0/1     ContainerCreating   0             1s
web-0   1/1     Running             0             1s
web-2   1/1     Terminating         1 (18m ago)   12h
web-2   0/1     Terminating         1 (18m ago)   12h
web-2   0/1     Terminating         1 (18m ago)   12h
web-2   0/1     Terminating         1 (18m ago)   12h
web-2   0/1     Terminating         1 (18m ago)   12h
web-1   1/1     Terminating         1 (18m ago)   12h
web-1   0/1     Terminating         1 (18m ago)   12h
web-1   0/1     Terminating         1 (18m ago)   12h
web-1   0/1     Terminating         1 (18m ago)   12h
web-1   0/1     Terminating         1 (18m ago)   12h
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             1s

当重新创建 web StatefulSet时,首先重新启动 web-0 。当 web-0 变成 RunningReady 时,由于 web-1 已经处于 RunningReady 状态,StatefulSet会接收这个pod。由于重新创建的StatefulSet的 replicas 等于 2 ,一旦 web-0 被重新创建,且 web-1 被认为已经处于 RunningReady 状态,则 web-2 将会被终止。

$ for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

尽管删除了StatefulSet和 web-0 pod,但它仍然使用最初写入 index.html 文件的hostname进行服务。这是因为StatefulSet永远不会删除和pod相关联的PV。当重建StatefulSet,并且重新启动了 web-0 时,它原本的PV会被重新mount。

级联删除

在监视窗口,重新监视:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          13m
web-1   1/1     Running   0          13m
kubectl delete statefulset web

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          13m
web-1   1/1     Running   0          13m
web-1   1/1     Terminating   0          13m
web-0   1/1     Terminating   0          13m
web-0   0/1     Terminating   0          13m
web-1   0/1     Terminating   0          13m
web-1   0/1     Terminating   0          13m
web-1   0/1     Terminating   0          13m
web-1   0/1     Terminating   0          13m
web-0   0/1     Terminating   0          13m
web-0   0/1     Terminating   0          13m
web-0   0/1     Terminating   0          13m

可见,pod按照序号索引相反的顺序依次终止。StatefulSet控制器会等待pod后继者(注:指序号加1的pod)完全终止,才会终止前一个pod。

注意:尽管级联删除会删除StatefulSet及其pod,但不会删除与StatefulSet关联的headless service。必须手动删除 nginx service。

kubectl delete service nginx

重新创建StatefulSet和headless service:

$ kubectl apply -f web.yaml
service/nginx created
statefulset.apps/web created
$ for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done
web-0
web-1

即使你已经完全删除了StatefulSet及其pod,重新创建pod时,并mount它们的PV,并且 web-0web-1 将继续使用hostname提供服务。

最后,删除 nginx service 和 web StatefulSet:

kubectl delete service nginxkubectl delete statefulset web

Pod 管理策略

对于某些分布式系统来说,确保StatefulSet的顺序性是不必要和/或不应该的。这些系统仅仅要求唯一性和身份标志。

为了避免这种严格顺序性,可通过 OrderedReady (默认)或 Parallel 来指定pod管理策略。

Parallel pod管理策略

Parallel pod管理策略告诉StatefulSet控制器并行启动或终止所有pod,不必等待pod变成 RunningReady 状态或者完全终止状态,就可以启动或终止另一个pod。该选项只影响缩放行为,不影响更新。

创建文件 web-parallel.yaml 如下:

apiVersion: v1
kind: Service
metadata:name: nginxlabels:app: nginx
spec:ports:- port: 80name: webclusterIP: Noneselector:app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:name: web
spec:serviceName: "nginx"podManagementPolicy: "Parallel"replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx# image: registry.k8s.io/nginx-slim:0.8image: docker.io/kaiding1/nginx-slim:0.8ports:- containerPort: 80name: webvolumeMounts:- name: wwwmountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: wwwspec:accessModes: [ "ReadWriteOnce" ]resources:requests:storage: 1Gi

在监视窗口,重新监视:

$ kubectl get pod -l app=nginx --watch
$ kubectl apply -f web-parallel.yaml
service/nginx created
statefulset.apps/web created

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   0/1     Pending   0          0s
web-0   0/1     Pending   0          0s
web-1   0/1     Pending   0          0s
web-0   0/1     ContainerCreating   0          1s
web-1   0/1     Pending             0          1s
web-1   0/1     ContainerCreating   0          1s
web-1   1/1     Running             0          2s
web-0   1/1     Running             0          2s

可见,StatefulSet控制器几乎同时启动了 web-0web-1

kubectl scale statefulset/web --replicas=4

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   0/1     Pending   0          0s
web-0   0/1     Pending   0          0s
web-1   0/1     Pending   0          0s
web-0   0/1     ContainerCreating   0          1s
web-1   0/1     Pending             0          1s
web-1   0/1     ContainerCreating   0          1s
web-1   1/1     Running             0          2s
web-0   1/1     Running             0          2s
web-2   0/1     Pending             0          0s
web-3   0/1     Pending             0          0s
web-2   0/1     Pending             0          0s
web-3   0/1     Pending             0          0s
web-2   0/1     ContainerCreating   0          0s
web-3   0/1     ContainerCreating   0          0s
web-2   1/1     Running             0          1s
web-3   1/1     Running             0          2s

StatefulSet启动了两个新的pod,而且在启动第二个之前并没有等待第一个变成 RunningReady 状态。

清理

kubectl delete sts web

回到监视窗口,如下:

$ kubectl get pod -l app=nginx --watch
NAME    READY   STATUS    RESTARTS   AGE
web-0   0/1     Pending   0          0s
web-0   0/1     Pending   0          0s
web-1   0/1     Pending   0          0s
web-0   0/1     ContainerCreating   0          1s
web-1   0/1     Pending             0          1s
web-1   0/1     ContainerCreating   0          1s
web-1   1/1     Running             0          2s
web-0   1/1     Running             0          2s
web-2   0/1     Pending             0          0s
web-3   0/1     Pending             0          0s
web-2   0/1     Pending             0          0s
web-3   0/1     Pending             0          0s
web-2   0/1     ContainerCreating   0          0s
web-3   0/1     ContainerCreating   0          0s
web-2   1/1     Running             0          1s
web-3   1/1     Running             0          2s
web-3   1/1     Terminating         0          2m12s
web-1   1/1     Terminating         0          4m25s
web-0   1/1     Terminating         0          4m25s
web-2   1/1     Terminating         0          2m12s
web-3   0/1     Terminating         0          2m12s
web-0   0/1     Terminating         0          4m25s
web-2   0/1     Terminating         0          2m12s
web-1   0/1     Terminating         0          4m25s
web-0   0/1     Terminating         0          4m26s
web-0   0/1     Terminating         0          4m26s
web-0   0/1     Terminating         0          4m26s
web-2   0/1     Terminating         0          2m13s
web-2   0/1     Terminating         0          2m13s
web-2   0/1     Terminating         0          2m13s
web-1   0/1     Terminating         0          4m26s
web-1   0/1     Terminating         0          4m26s
web-1   0/1     Terminating         0          4m26s
web-3   0/1     Terminating         0          2m13s
web-3   0/1     Terminating         0          2m13s
web-3   0/1     Terminating         0          2m13s

可见,StatefulSet并发的删除所有pod。

kubectl delete svc nginx
$ kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-71418599-e0d4-4c45-9f3b-f56ebd992509   1Gi        RWO            standard       23h
www-web-1   Bound    pvc-c6741fad-942a-434a-b2a7-05efe540ca1e   1Gi        RWO            standard       23h
www-web-2   Bound    pvc-3164d5af-59b1-4984-ad2a-8a54ae2f0643   1Gi        RWO            standard       19h
www-web-3   Bound    pvc-5e5253e5-290c-4bd1-9727-c25a751e5d64   1Gi        RWO            standard       19h
www-web-4   Bound    pvc-797b39a1-a55c-47c4-92ab-6e0e2ff567ee   1Gi        RWO            standard       19h
$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS   REASON   AGE
pvc-3164d5af-59b1-4984-ad2a-8a54ae2f0643   1Gi        RWO            Delete           Bound    default/www-web-2   standard                19h
pvc-5e5253e5-290c-4bd1-9727-c25a751e5d64   1Gi        RWO            Delete           Bound    default/www-web-3   standard                19h
pvc-71418599-e0d4-4c45-9f3b-f56ebd992509   1Gi        RWO            Delete           Bound    default/www-web-0   standard                23h
pvc-797b39a1-a55c-47c4-92ab-6e0e2ff567ee   1Gi        RWO            Delete           Bound    default/www-web-4   standard                19h
pvc-c6741fad-942a-434a-b2a7-05efe540ca1e   1Gi        RWO            Delete           Bound    default/www-web-1   standard                23h
$ kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
$ kubectl get pvc
No resources found in default namespace.
$ kubectl delete pv pvc-3164d5af-59b1-4984-ad2a-8a54ae2f0643 pvc-5e5253e5-290c-4bd1-9727-c25a751e5d64 pvc-71418599-e0d4-4c45-9f3b-f56ebd992509 pvc-797b39a1-a55c-47c4-92ab-6e0e2ff567ee pvc-c6741fad-942a-434a-b2a7-05efe540ca1e
persistentvolume "pvc-3164d5af-59b1-4984-ad2a-8a54ae2f0643" deleted
persistentvolume "pvc-5e5253e5-290c-4bd1-9727-c25a751e5d64" deleted
persistentvolume "pvc-71418599-e0d4-4c45-9f3b-f56ebd992509" deleted
persistentvolume "pvc-797b39a1-a55c-47c4-92ab-6e0e2ff567ee" deleted
persistentvolume "pvc-c6741fad-942a-434a-b2a7-05efe540ca1e" deleted
$ kubectl get pv
No resources found

参考

  • https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络(第六版)复习提纲13

前同步码&#xff0c;七位1010交替出现&#xff0c;帧开始码&#xff1a;10101011 为什么没有帧结束&#xff1f;曼彻斯特码传播完成后&#xff0c;维持高电平&#xff0c;不再跳变&#xff0c;因此不必要设置帧结束。 3.无效的MAC帧 i.数据字段的长度与长度字段的值不一致&…

RDMA vs InfiniBand 网卡接口如何区分?

(该架构图来源于参考文献) 高性能计算网络&#xff0c;RoCE vs. InfiniBand该怎么选&#xff1f; 新 RoCEv2 标准可实现 RDMA 路由在第三层以太网网络中的传输。RoCEv2 规范将用以太网链路层上的 IP 报头和 UDP 报头替代 InfiniBand 网络层。这样&#xff0c;就可以在基于 IP…

Android (6) 弹窗 onJsAlert,onJsConfirm,onJsPrompt

目录 1 网页的3种弹窗 1.1 Alert警示弹窗 1.2 Confirm确认弹窗 1.3 Prompt输入弹窗 2 WebView支持弹窗 2.1 onJsAlert 2.2 onJsConfirm 2.3 onJsPrompt AndroidApp内嵌一个WebView用于承载网页,WebView会监听拦截网页的3种弹窗(Alert,Confirm,Prompt),如果不做任何处理…

Java算法---递归算法基础介绍

目录 一、递归算法 二、递归算法的典型例子 &#xff08;1&#xff09;阶乘 &#xff08;2&#xff09;二分查找 &#xff08;3&#xff09;冒泡排序 &#xff08;4&#xff09;插入排序 一、递归算法 计算机科学中&#xff0c;递归是一种解决计算问题的方法。其中解决方案…

GM/T 0018-2012 设备接口描述笔记

GM/T 0018-2012 设备接口描述笔记 文章目录 GM/T 0018-2012 设备接口描述笔记6. 设备接口描述6.1 密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置6.2 设备管理类函数6.3 密钥管理类函数6.4 非对称算法运算类函数6.5 对称算法运算类函数6.6 杂凑运算类函数6.7 用户…

数据库函数查询资料

MySQL 函数 MySQL 有很多内置的函数&#xff0c;以下列出了这些函数的说明。 MySQL 字符串函数 函数描述实例ASCII(s)返回字符串 s 的第一个字符的 ASCII 码。 返回 CustomerName 字段第一个字母的 ASCII 码&#xff1a; SELECT ASCII(CustomerName) AS NumCodeOfFirstChar F…

ServletResponse接口

ServletResponse接口 ServletContext接口向servlet提供关于其运行环境的信息。上下文也称为Servlet上下文或Web上下文,由Web容器创建,用作ServletContext接口的对象。此对象表示Web应用程序在其执行的上下文。Web容器为所部署的每个Web应用程序创建一个ServletContext对象。…

Linux npm install 时报错 reason: certificate has expired

错误 [rootxxxx devtools]# npm install -g vuepress --no-check-certificate npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/vuepress failed, reason: certificate has expired解决方案 根据错误原…

C# 只读文件删除提示失败,给文件修改属性

需求背景&#xff1a;处理文件后&#xff0c;删除源文件信息&#xff0c;但不能确保源文件是只读文件&#xff0c;因此需要修改文件属性 //设置文件属性 string path "文件路径"; File.SetAttributes(path, FileAttributes.Normal); //删除文件 File.Delete(path);参…

NineData支持制定安全、可靠的SQL开发规范

在和数据库打交道中&#xff0c;不管是数据库管理员&#xff08;DBA&#xff09;还是开发人员&#xff0c;经常会做一些CURD操作。因为每个人对数据库的了解程度不一样&#xff0c;所以在项目上线时&#xff0c;往往还需要专职人员对数据库的CURD操作进行审核&#xff0c;确保C…

vue3+naiveUI二次封装的v-model 联动输入框

根据官网说明使用 源码 <template><div class"clw-input pt-3"><n-inputref"input":value"modelValue":type"type":title"title"clearable:disabled"disabled":size"size"placeholder&…

RISC-V常用汇编指令

RISC-V寄存器表&#xff1a; RISC-V和常用的x86汇编语言存在许多的不同之处&#xff0c;下面将列出其中部分指令作用&#xff1a; 指令语法描述addiaddi rd,rs1,imm将寄存器rs1的值与立即数imm相加并存入寄存器rdldld t0, 0(t1)将t1的值加上0,将这个值作为地址&#xff0c;取…

【JaveWeb教程】(32)SpringBootWeb案例之《智能学习辅助系统》的详细实现步骤与代码示例(5)文件上传的实现

目录 SpringBootWeb案例052. 文件上传2.1 简介2.2 本地存储 SpringBootWeb案例05 前面我们已经实现了员工信息的条件分页查询以及删除操作。 关于员工管理的功能&#xff0c;还有两个需要实现新增和修改员工。 本节的主要内容&#xff1a; 文件上传 2. 文件上传 在我们完成…

详解 C++ 中移动构造与移动赋值

1. 基本介绍 移动赋值语法原理 移动赋值语法的原理是基于右值引用&#xff08;rvalue reference&#xff09;的概念。在C11中引入了右值引用&#xff0c;通过使用双 ampersands&#xff08;&&&#xff09;来声明一个右值引用。 右值引用 右值引用可以绑定到临时对象&a…

从 React 到 Qwik:开启高效前端开发的新篇章

1. Qwik Qwik 是一个为构建高性能的 Web 应用程序而设计的前端 JavaScript 框架,它专注于提供即时启动性能,即使是在移动设备上。Qwik 的关键特性是它采用了称为“恢复性”的技术,该技术消除了传统前端框架中常见的 hydration 过程。 恢复性是一种序列化和恢复应用程序状态…

日常学习之:如何使用 dockerfile 将 vue 的单独前端项目通过 docker 的方式部署到 heroku上

文章目录 需求描述开始操作准备阶段&#xff1a;准备 server.js 文件并安装依赖&#xff0c;将 vue 项目包装成单独的服务器制作 server.js安装 server.js 需要的依赖 构建 Dockerfileheroku container 链接和部署其他细节 需求描述 你想用 vue 构建前端&#xff0c;用 django…

设计模式六(模板方法模式)

是一种行为型的设计模式&#xff0c;将一些步骤的实现延迟到子类有些步骤是固定的可以抽取父类&#xff0c;在父类中实现各个步骤&#xff0c;并且在父类中定义一个流程控制方法&#xff0c;将某个容易发生变化的步骤定义为抽象方法&#xff0c;针对不同的子类实现不同的方法&a…

【设计模式】阿里终面:你觉得这个例子是策略模式吗?

什么是策略模式&#xff1f; 策略模式&#xff0c;举几个贴近生活的例子&#xff1a;当我们出行的时候&#xff0c;不同的出行方式就是不同的策略&#xff0c;例如走路、开车、骑自行车、坐飞机、坐邮轮等等&#xff0c;每一种出行方式都代表着不同的费用和时间&#xff1b;当…

关于css 的基础试题

CSS是什么的缩写&#xff1f; A. Creative Style SheetsB. Cascading Style SheetsC. Computer Style SheetsD. Colorful Style Sheets 在HTML中&#xff0c;通过什么标签引入CSS样式&#xff1f; A. <script>B. <style>C. <link>D. <css> 以下哪个选项…

make: *** No rule to make target ‘clean‘. Stop.

项目场景&#xff1a; 在Ubuntu下编写makefile文件编译的时候,出现make: *** No rule to make target ‘clean’. Stop. 问题描述 make: *** No rule to make target ‘clean’. Stop. 解决方案&#xff1a; 原本我makefile文件的名字是MakeFile , 把它改为makefile以后完美运…