在 Kubernetes 中,ReplicaSet
是一个控制器资源,不是一个实际运行的 Pod 或容器。ReplicaSet
控制器是 Kubernetes 控制平面的一部分,运行在 Kubernetes 主节点(Master 节点)上。它负责确保指定数量的 Pod 副本始终运行在集群中,并根据定义的规格(如标签选择器、模板等)管理这些 Pod。
replicaset管理pod的弊端
当replicaset创建的pod运行起来后,可以更改pod的副本数量,如果增加了,就新建pod,如果减少了,就删除pod,但是如果更改了pod中container的镜像,kubectl apply后,其实是不会生效的,因为本身运行的pod并没有被更换,还是更改之前的pod编号。
kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
frontend-6pvr6 1/1 Running 0 33m 10.244.166.161 node1 <none> <none>
frontend-6q8jx 1/1 Running 0 33m 10.244.166.160 node1 <none> <none>
frontend-bcctp 1/1 Running 0 33m 10.244.104.19 node2 <none> <none>
curl 10.244.166.161
<html ng-app="redis">
<head>
<title>Guestbook</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.12/angular.min.js"></script>
<script src="controllers.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.0/ui-bootstrap-tpls.js"></script>
</head>
<body ng-controller="RedisCtrl">
<div style="width: 50%; margin-left: 20px">
<h2>Guestbook</h2>
<form>
<fieldset>
<input ng-model="msg" placeholder="Messages" class="form-control" type="text" name="input"><br>
<button type="button" class="btn btn-primary" ng-click="controller.onRedis()">Submit</button>
</fieldset>
</form>
<div>
<div ng-repeat="msg in messages track by $index">
{{msg}}
</div>
</div>
</div>
</body>
</html>
修改yaml中的image
containers:
- name: php-redis
# image: docker.io/yecc/gcr.io-google_samples-gb-frontend:v3
image: docker.io/ikubernetes/myapp:v2
imagePullPolicy: IfNotPresent
apply之后,查看pods,发现并没有变化,访问10.244.166.161,发现还是原来的网页页面,说明还是之前的镜像。
kubectl apply -f replicat.yaml
replicaset.apps/frontend configured
[root@master yam_files]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
frontend-6pvr6 1/1 Running 0 37m 10.244.166.161 node1 <none> <none>
frontend-6q8jx 1/1 Running 0 37m 10.244.166.160 node1 <none> <none>
frontend-bcctp 1/1 Running 0 37m 10.244.104.19 node2 <none> <none>
删除原来的replicaset,重新部署,才会生效
kubectl delete replicaset frontend
replicaset.apps "frontend" deleted
[root@master yam_files]# kubectl apply -f replicat.yaml
replicaset.apps/frontend created
[root@master yam_files]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
frontend-5n75c 0/1 Running 0 14s 10.244.166.163 node1 <none> <none>
frontend-k7gw7 0/1 Running 0 14s 10.244.166.162 node1 <none> <none>
frontend-pqvqm 0/1 Running 0 14s 10.244.104.20 node2 <none> <none>
curl 10.244.166.163
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
所以生产环境如果升级,如果用replicaset部署,就需要手动删掉全部pod,非常不方便,因此更高级的方式是使用deployment。
Deployment
deployment是k8s中最常用的资源对象,支持自定义滚动更新。
![](https://img-blog.csdnimg.cn/direct/8979e8a765d34ba0bad149236f194abe.png)
deployment滚动更新策略的两个参数:
-
maxUnavailable:
- 在滚动更新期间允许不可用的最大 Pod 数量,可以是绝对值(整数)或相对值(百分比)。
- 默认值是 25%。
-
maxSurge:
- 在滚动更新期间可以超过期望 Pod 数量的最大 Pod 数量,可以是绝对值(整数)或相对值(百分比)。
maxSurge
数量的额外 Pod,终止maxUnavailable
数量的旧 Pod。等待新创建的 Pod 变为可用状态。重复上述步骤,直到所有旧的 Pod 都被替换为新的 Pod。确保在更新过程中,应用程序尽量保持可用,最小化服务中断。
写一个deployment文件如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-v1
labels:
app: myapp
spec:
replicas: 2
selector:
matchLabels:
app: myapp
version: v1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
name: test
labels:
app: myapp
version: v1
spec:
containers:
- name: myapp
image: janakiramm/myapp:v1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
startupProbe:
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
livenessProbe:
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
readinessProbe:
periodSeconds: 5
initialDelaySeconds: 20
timeoutSeconds: 10
httpGet:
scheme: HTTP
port: 80
path: /
随后apply,产生一个replicaset,然后修改里面的镜像版本为v2,重新apply,pod的滚动更新如下,删掉一个,创建一个,再删掉一个,创建一个。这是由于上面的yaml文件设置了maxUnavailable为1,有些时候这样做会导致服务不可用,所以一般都是设置这个值为0。
查看replicaset,可以看到两个版本,但是只有一个在运行
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
myapp-v1-6657946df4 2 2 2 15m
myapp-v1-8559f5cf7f 0 0 0 16m
可以看到cf7f是版本较旧的replicaset,查看yaml文件
kubectl get replicaset myapp-v1-8559f5cf7f -o yaml > 2.yaml
cat 2.yaml
>
apiVersion: apps/v1
kind: ReplicaSet
metadata:
annotations:
deployment.kubernetes.io/desired-replicas: "2"
deployment.kubernetes.io/max-replicas: "3"
deployment.kubernetes.io/revision: "1"
creationTimestamp: "2024-06-25T15:21:05Z"
generation: 3
labels:
app: myapp
pod-template-hash: 8559f5cf7f
version: v1
name: myapp-v1-8559f5cf7f
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: Deployment
name: myapp-v1
uid: 7e0809ec-be5f-4131-b456-897637743878
resourceVersion: "1226151"
uid: 89527cd9-9b9f-47d4-8c95-c8b77602a0bc
spec:
replicas: 0
selector:
matchLabels:
app: myapp
pod-template-hash: 8559f5cf7f
version: v1
template:
metadata:
creationTimestamp: null
labels:
app: myapp
pod-template-hash: 8559f5cf7f
version: v1
name: test
spec:
containers:
- image: janakiramm/myapp:v2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: 80
scheme: HTTP
initialDelaySeconds: 20
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 10
name: myapp
ports:
- containerPort: 80
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: 80
scheme: HTTP
initialDelaySeconds: 20
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 10
resources: {}
startupProbe:
failureThreshold: 3
httpGet:
path: /
port: 80
scheme: HTTP
initialDelaySeconds: 20
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 10
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
status:
observedGeneration: 3
replicas: 0
这里面的字段可以看出它已经经历了三次更新,这两字段是一样的,代表replicaset的版本正确。
metadata:
generation: 3
status:
observedGeneration: 3
deployment回滚
查看replicaset,发现同一个deployment有好几个replicaset,使用rollout命令,可以查看deployment的更新版本,并且使用rollout undo来回滚到指定的版本,可以看到回滚的时候也是滚动更新。
kubectl get rs
NAME DESIRED CURRENT READY AGE
my-deployment-6b4db9b59 0 0 0 93m
my-deployment-7f6d8d7647 0 0 0 4m9s
my-deployment-8cd4898cd 2 2 2 62s
[root@master yam_files]# kubectl rollout history deployment my-deployment
deployment.apps/my-deployment
REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
[root@master yam_files]# kubectl rollout undo deployment my-deployment --to-revision=1
deployment.apps/my-deployment rolled back
[root@master yam_files]# kubectl get rs
NAME DESIRED CURRENT READY AGE
my-deployment-6b4db9b59 2 2 0 95m
my-deployment-7f6d8d7647 0 0 0 6m43s
my-deployment-8cd4898cd 1 1 1 3m36s
[root@master yam_files]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-deployment-6b4db9b59-ddb96 1/1 Running 0 107s
my-deployment-6b4db9b59-jwkt4 1/1 Running 0 107s
[root@master yam_files]# kubectl rollout history deployment my-deployment
deployment.apps/my-deployment
REVISION CHANGE-CAUSE
2 <none>
3 <none>
4 <none>
[root@master yam_files]# kubectl rollout undo deployment my-deployment --to-revision=3
deployment.apps/my-deployment rolled back
[root@master yam_files]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
my-deployment-6b4db9b59-ddb96 1/1 Running 0 2m21s
my-deployment-8cd4898cd-46g5n 0/1 Running 0 5s
my-deployment-8cd4898cd-mhkbs 0/1 Running 0 5s
my-deployment-8cd4898cd-46g5n 0/1 Running 0 25s
my-deployment-8cd4898cd-46g5n 1/1 Running 0 25s
my-deployment-6b4db9b59-ddb96 1/1 Terminating 0 2m41s
my-deployment-8cd4898cd-mhkbs 0/1 Running 0 25s
my-deployment-8cd4898cd-mhkbs 1/1 Running 0 25s
my-deployment-6b4db9b59-ddb96 1/1 Terminating 0 2m42s
my-deployment-6b4db9b59-ddb96 0/1 Terminating 0 2m43s
my-deployment-6b4db9b59-ddb96 0/1 Terminating 0 2m43s
my-deployment-6b4db9b59-ddb96 0/1 Terminating 0 2m43s