Kubernetes(5) Stateless Pod Controller -> ReplicaSet, DaemonSet, Deployment
1 Pod 控制器入门
ReplicaSet and Deployment (stateless) 可以管理无状态用户资源。Deployment是 ReplicaSet的 high level 版本实现,且封装了一些附加的常用功能。在实战中往往推荐使用Deployment而不是ReplicaSet。见 https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
DaemonSet (stateless) : 可以确保Pod能平均分布在每一个定义好的节点上启动,这种类型的控制器可以用作管理一些服务如: Beats 这样收集节点日志的服务
Jobs, CronJob: 一次性,或定期完成特定工作
StatefulSets, Operator -> 特殊用于 stateful 应用
2 ReplicaSet
ReplicaSet中有三个重要配置即:
- replicas: 需要运行的副本数,可以设置为0,默认为1
- selector: 按照标签j键值筛选匹配上的Pod 结果。标签的键值必须匹配才能控制到相应ReplicaSet,如果选择结果没有匹配,则返回空对象。 详见: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors .
- template: 模块用于定义Pod,包括Pod的名字,Pod拥有的label以及Pod中运行的应用 详见: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template
一个例子用于展示如何创建 ReplicaSet,其中 spec.selector.matchLabels 决定哪个 Pod 会被选中如标签含有-> app: rs-demo-app 与 release: canary
spec.template.metadata.labels 定义了Pod 的标签.
使用如下模板生成2 副本的 ReplicaSet:
apiVersion: apps/v1 kind: ReplicaSet metadata: name: rs-demo-app namespace: default spec: replicas: 2 selector: matchLabels: app: rs-demo-app release: canary template: metadata: name: rs-demo-app-pod labels: app: rs-demo-app release: canary enviroment: qa spec: containers: - name: rs-demo-app-container image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3
命令行演示:
[root@k8smaster controllers]# kubectl create -f replicaset-demo.yaml replicaset.apps/rs-demo-app created [root@k8smaster controllers]# kubectl get rs NAME DESIRED CURRENT READY AGE nginx-79976cbb47 3 3 3 4d nginx-f4bd648c7 0 0 0 4d rs-demo-app 2 2 2 12s [root@k8smaster controllers]# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-79976cbb47-8dqnk 1/1 Running 1 4d nginx-79976cbb47-p247g 1/1 Running 1 4d nginx-79976cbb47-ppbqv 1/1 Running 1 4d rs-demo-app-c2crz 1/1 Running 0 26s rs-demo-app-xzddj 1/1 Running 0 26s
[root@k8smaster controllers]# kubectl describe rs-demo-app-c2crz error: the server doesn't have a resource type "rs-demo-app-c2crz"
[root@k8smaster controllers]# kubectl describe po rs-demo-app-c2crz Name: rs-demo-app-c2crz Namespace: default Priority: 0 PriorityClassName: <none> Node: k8snode2/172.16.0.13 Start Time: Mon, 07 Jan 2019 22:44:25 +0100 Labels: app=rs-demo-app enviroment=qa release=canary Annotations: <none> Status: Running IP: 10.244.2.17 Controlled By: ReplicaSet/rs-demo-app Containers: rs-demo-app-container: Container ID: docker://b82932a65bf6f02ccbb05ef6d39d2c27f42e90cc9a0e4a21924928133e29f9fa Image: ikubernetes/myapp:v1 Image ID: docker-pullable://ikubernetes/myapp@sha256:9c3dc30b5219788b2b8a4b065f548b922a34479577befb54b03330999d30d513 Port: 80/TCP Host Port: 0/TCP State: Running Started: Mon, 07 Jan 2019 22:44:26 +0100 Ready: True Restart Count: 0 Liveness: http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-rxs5t (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: default-token-rxs5t: Type: Secret (a volume populated by a Secret) SecretName: default-token-rxs5t Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 104s default-scheduler Successfully assigned default/rs-demo-app-c2crz to k8snode2 Normal Pulled 88s kubelet, k8snode2 Container image "ikubernetes/myapp:v1" already present on machine Normal Created 88s kubelet, k8snode2 Created container Normal Started 87s kubelet, k8snode2 Started container
3 手动给 ReplicaSet 扩容
给ReplicaSet 扩容可以用 kubectl api 直接编辑。比如这里我们使用了 kubectl edit rs 然后将 spec.replicas 从2 上调至 3, 这样会自动启动一个新的副本。
[root@k8smaster controllers]# kubectl edit rs rs-demo-app replicaset.extensions/rs-demo-app edited # then change spec.replicas from 2 to 3 [root@k8smaster controllers]# kubectl get pod --show-labels NAME READY STATUS RESTARTS AGE LABELS nginx-79976cbb47-8dqnk 1/1 Running 1 4d pod-template-hash=3553276603,run=nginx nginx-79976cbb47-p247g 1/1 Running 1 4d pod-template-hash=3553276603,run=nginx nginx-79976cbb47-ppbqv 1/1 Running 1 4d pod-template-hash=3553276603,run=nginx rs-demo-app-c2crz 1/1 Running 0 14m app=rs-demo-app,enviroment=qa,release=canary rs-demo-app-lkh5j 1/1 Running 0 9s app=rs-demo-app,enviroment=qa,release=canary rs-demo-app-xzddj 1/1 Running 0 14m app=rs-demo-app,enviroment=qa,release=canary
* 同理也可以通过修改 image 字段来更新应用的版本号,后面的文章会逐渐介绍这以功能
4 Deployments
Deployments 是加强版的 ReplicatSet 且拥有ReplicaSet的所有功能。也就是说,Deployment 可以用于执行一系列 ReplicaSet 组合操作如:Rollingupdate, 各种类型发布 (blue/green, canary etc.).
下面列举了 Deployment 的应用场景:
- Create a Deployment to rollout a ReplicaSet.
- Declare the new state of the Pods
- Rollback to an earlier Deployment revision
- Scale up the Deployment to facilitate more load.
- Pause the Deployment
- Use the status of the Deployment
- Clean up older ReplicaSets
下面的例子展示了部署Deployment 且配置两个replica 副本:
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: myapp release: canary template: metadata: labels: app: myapp release: canary spec: containers: - name: myapp-container image: ikubernetes/myapp.v1 ports: - name: http containerPort: 80
部署命令:
kubectl apply -f deployments-demo.yaml
检查部署情况:
[root@k8smaster controllers]# kubectl get deploy -o wide NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR myapp-deploy 2 2 2 0 5m myapp-container ikubernetes/myapp.v1 app=myapp,release=canary
[root@k8smaster controllers]# kubectl get rs -o wide NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR myapp-deploy-8546d69b76 2 2 0 5m myapp-container ikubernetes/myapp.v1 app=myapp,pod-template-hash=4102825632,release=canary
[root@k8smaster controllers]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE
myapp-deploy-f5f97bb54-2tlh6 1/1 Running 0 18s 10.244.2.23 k8snode2
myapp-deploy-f5f97bb54-65ffd 1/1 Running 0 18s 10.244.1.37 k8snode1
* 部署成功后发现,经过我们只写了部署脚本,但是同时自动生成了ReplicaSet对象,这也充分证明Deployment是ReplicaSet的升级版
* 名字: myapp-deploy-8546d69b76 来源于Deployment名字加 Pod 自动生成的 hash-code
5 Deployments 扩容
可以用修改模板然后靠 apply 命令完成自动扩容,比如修改模板中的 replicas 参数从 2 到 3 然后部署:
kubectl apply -f deployment-demo.yaml
与ReplicaSet不同 用户可以重复使用这个命令来进行 Update操作,且不需要删除现有的 ReplicaSet,这个操作已经封装在 Deployment中了。
[root@k8smaster controllers]# kubectl describe deploy myapp-deploy Name: myapp-deploy Namespace: default CreationTimestamp: Tue, 08 Jan 2019 06:02:20 +0100 Labels: <none> Annotations: deployment.kubernetes.io/revision: 1 kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"myapp-deploy","namespace":"default"},"spec":{"replicas":2... Selector: app=myapp,release=canary Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=myapp release=canary Containers: myapp-container: Image: ikubernetes/myapp:v1 Port: 80/TCP Host Port: 0/TCP Environment: <none> Mounts: <none> Volumes: <none> Conditions: Type Status Reason ---- ------ ------ Available True MinimumReplicasAvailable Progressing True NewReplicaSetAvailable OldReplicaSets: <none> NewReplicaSet: myapp-deploy-f5f97bb54 (2/2 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 7m36s deployment-controller Scaled up replica set myapp-deploy-f5f97bb54 to 2
* if you change the template and deploy it again, kubernetes will remark the change information in Annotations -> like if you set replicas from 2 to 3...
* StrategyType by default is RollingUpdate and RollingUpdateStrategy: 25% max unavailable, 25% max surge
6 Deployments Update
现在我们修改模板的 replicas 从 2 到3, app version 从ikubernetes/myapp:v1 到 ikubernetes/myapp:v2 ,之后让这个修改生效,观察 Deployment的变动.
另开一个窗口 用 -w 命令观察 Pod 被终止和重启的过程:
[root@k8smaster ~]# kubectl get pod -l app=myapp -w NAME READY STATUS RESTARTS AGE myapp-deploy-f5f97bb54-2tlh6 1/1 Running 0 18m myapp-deploy-f5f97bb54-65ffd 1/1 Running 0 18m
回之前的窗口执行部署命令:
[root@k8smaster controllers]# kubectl apply -f deployments-demo.yaml
deployment.apps/myapp-deploy configured
我们可以看到新窗口的变化:
[root@k8smaster ~]# kubectl get pod -l app=myapp -w NAME READY STATUS RESTARTS AGE myapp-deploy-f5f97bb54-2tlh6 1/1 Running 0 18m myapp-deploy-f5f97bb54-65ffd 1/1 Running 0 18m myapp-deploy-f5f97bb54-7lhmz 0/1 Pending 0 0s myapp-deploy-f5f97bb54-7lhmz 0/1 Pending 0 0s myapp-deploy-f5f97bb54-7lhmz 0/1 ContainerCreating 0 0s myapp-deploy-f5f97bb54-7lhmz 1/1 Running 0 2s myapp-deploy-5c574dbf-trf5k 0/1 Pending 0 0s myapp-deploy-5c574dbf-trf5k 0/1 Pending 0 0s myapp-deploy-5c574dbf-trf5k 0/1 ContainerCreating 0 0s myapp-deploy-5c574dbf-trf5k 1/1 Running 0 5s myapp-deploy-f5f97bb54-7lhmz 1/1 Terminating 0 7m12s myapp-deploy-5c574dbf-p7jpw 0/1 Pending 0 0s myapp-deploy-5c574dbf-p7jpw 0/1 Pending 0 0s myapp-deploy-5c574dbf-p7jpw 0/1 ContainerCreating 0 0s myapp-deploy-f5f97bb54-7lhmz 0/1 Terminating 0 7m14s myapp-deploy-f5f97bb54-7lhmz 0/1 Terminating 0 7m15s myapp-deploy-f5f97bb54-7lhmz 0/1 Terminating 0 7m15s myapp-deploy-5c574dbf-p7jpw 1/1 Running 0 4s myapp-deploy-f5f97bb54-2tlh6 1/1 Terminating 0 28m myapp-deploy-5c574dbf-j25bz 0/1 Pending 0 0s myapp-deploy-5c574dbf-j25bz 0/1 Pending 0 0s myapp-deploy-5c574dbf-j25bz 0/1 ContainerCreating 0 0s myapp-deploy-f5f97bb54-2tlh6 0/1 Terminating 0 28m myapp-deploy-5c574dbf-j25bz 1/1 Running 0 1s myapp-deploy-f5f97bb54-65ffd 1/1 Terminating 0 28m myapp-deploy-f5f97bb54-65ffd 0/1 Terminating 0 28m myapp-deploy-f5f97bb54-2tlh6 0/1 Terminating 0 28m myapp-deploy-f5f97bb54-2tlh6 0/1 Terminating 0 28m myapp-deploy-f5f97bb54-65ffd 0/1 Terminating 0 28m myapp-deploy-f5f97bb54-65ffd 0/1 Terminating 0 28m
通过上面信息可以看到 Deployment 会自动创建一个新 Pod 然后干掉一个旧的
Update结束后会达到如下状态:
[root@k8smaster ~]# kubectl get rs -o wide NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR myapp-deploy-5c574dbf 3 3 3 50m myapp-container ikubernetes/myapp:v2 app=myapp,pod-template-hash=17130869,release=canary myapp-deploy-f5f97bb54 0 0 0 1h myapp-container ikubernetes/myapp:v1 app=myapp,pod-template-hash=919536610,release=canary
[root@k8smaster ~]# kubectl get deploy -o wide NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR myapp-deploy 3 3 3 3 1h myapp-container ikubernetes/myapp:v2 app=myapp,release=canary
* ReplicaSet myapp-deploy-f5f97bb54 下面没有任何 Pods 新部署的ReplicaSet myapp-deploy-5c574dbf 升级到了新的版本
* 旧 ReplicaSet 依然会被显示,在特定情况下可以使用 Rollback 返回到上一个版本
用户也可以通过使用 kubectl rollout history deploy <deployment name> 检查版本信息:
[root@k8smaster ~]# kubectl rollout history deploy myapp-deploy deployment.extensions/myapp-deploy REVISION CHANGE-CAUSE 1 <none> 2 <none>
使用 kubectl rollout undo 可以做回滚操作,默认会返回上一版本:
[root@k8smaster ~]# kubectl get rs -o wide NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR myapp-deploy-5c574dbf 0 0 0 1h myapp-container ikubernetes/myapp:v2 app=myapp,pod-template-hash=17130869,release=canary myapp-deploy-f5f97bb54 3 3 3 1h myapp-container ikubernetes/myapp:v1 app=myapp,pod-template-hash=919536610,release=canary
[root@k8smaster ~]# kubectl get deploy -o wide NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR myapp-deploy 3 3 3 3 1h myapp-container ikubernetes/myapp:v1 app=myapp,release=canary
现在我们回滚到 v2 使用命令 kubectl apply -f deployments-demo.yam...
7 Deployments Patch
也可以使用另一种方法升级或者扩容 Deploymets -> kubectl patch + json<content>
比如现在增加副本数从 3 到 5:
[root@k8smaster ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}' deployment.extensions/myapp-deploy patched
[root@k8smaster ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE myapp-deploy-5c574dbf-5q6vm 1/1 Running 0 14m 10.244.1.42 k8snode1 myapp-deploy-5c574dbf-7t9qw 1/1 Running 0 8s 10.244.2.29 k8snode2 myapp-deploy-5c574dbf-p9mfb 1/1 Running 0 14m 10.244.1.41 k8snode1 myapp-deploy-5c574dbf-ssxkc 1/1 Running 0 8s 10.244.1.43 k8snode1 myapp-deploy-5c574dbf-tcbhz 1/1 Running 0 14m 10.244.2.28 k8snode2
8 用Deployment部署 Canary Release
1. 编写新的部署模板
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: canary template: metadata: labels: app: myapp release: canary spec: containers: - name: myapp-container image: ikubernetes/myapp:v2 ports: - name: http containerPort: 80 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 0
2. 将镜像版本从 v2 变为 v3
3. pause Deployments:
# start rollout kubectl apply -f deployment-demo.yaml # pause after the first update been performed kubectl rollout pause deployment myapp-deploy # till here kubernetes will update one from Deployments-Pod
4. 恢复 Rollout Update
没有"pause" k8s将会按顺序删除一个Pod 然后添加一个新的 Pod 用于Update
但是现在有了 Pause 我们停止在当前状态下,该状态经过观察一共有 6 个Pod
生产环境中这个方法可以用于测试,比如在10个小时之后如果Update的版本工作顺利,则可以继续进行发布,使用以下命令:
kubectl rollout resume deployment myapp-deploy
这个命令会更新剩下的 Pods 到新的版本。最后可以使用后边的命令来检查当前的更新状态
kubectl get rs -o wide
9 Rollback
你可以使用 kubectl rollout undo 回滚到上一个版本
kubectl rollout undo deployment myapp-deploy
或者回滚至特定版本,你可以用以下命令来展示版本历史
kubectl rollout history deployment myapp-deploy
记住版本号,然后用这个版本号回滚至之前的状态
kubectl rollout undo deployment myapp-deploy --to-revision=1
10 DaemonSet
DeamonSet 保证所有或者被特定标记的节点上必定运行特定Pod 资源。当新节点加入集群时,Pod 也会相应添加进来。在节点被移除时,Pod 也会被相应地回收。在删除DaemonSet时,Pod 会随之被删除。
用下面模板创建 DaemonSet on Kubernetes Cluster
apiVersion: apps/v1 kind: DaemonSet metadata: name: myapp-ds namespace: default spec: selector: matchLabels: app: filebeat release: stable template: metadata: labels: app: filebeat release: stable spec: containers: - name: filebeat image: ikubernetes/filebeat:v5.6.5-alpine env: - name: REDIS_HOST value: redis.default.svc.cluster.local - name: REDIS_LOG_LEVEL value: info