TAPP

Kubernetes现有应用类型(如:Deployment、StatefulSet等)无法满足很多非微服务应用的需求,比如:操作(升级、停止等)应用中的指定pod、应用支持多版本的pod。如果要将这些应用改造为适合于这些workload的应用,需要花费很大精力,这将使大多数用户望而却步。

为解决上述复杂应用管理场景,基于Kubernetes CRD开发了一种新的应用类型TAPP,它是一种通用类型的workload,同时支持service和batch类型作业,满足绝大部分应用场景,它能让用户更好的将应用迁移到Kubernetes集群。

5.1. TAPP - 图1

TAPP 特点

功能点DeploymentStatefulSetTAPP
Pod唯一性每个Pod有唯一标识每个Pod有唯一标识
Pod存储独占仅支持单容器支持支持
存储随Pod迁移不支持支持支持
自动扩缩容支持不支持支持
批量升级支持不支持支持
严格顺序更新不支持支持不支持
自动迁移问题节点支持不支持支持
多版本管理同时只有1个版本可保持2个版本可保持多个版本
Pod原地升级不支持不支持支持

如果用Kubernetes的应用类型类比,TAPP ≈ Deployment + StatefulSet + Job ,它包含了Deployment、StatefulSet、Job的绝大部分功能,同时也有自己的特性,并且和原生Kubernetes相同的使用方式完全一致。

  1. 实例具有可以标识的id

    实例有了id,业务就可以将很多状态或者配置逻辑和该id做关联,当容器迁移时,通过TAPP的容器实例标识,可以识别该容器原来对应的数据,实现带云硬盘或者数据目录迁移

  2. 每个实例可以绑定自己的存储

    通过TAPP的容器实例标识,能很好地支持有状态的作业。在实例发生跨机迁移时,云硬盘能跟随实例一起迁移

  3. 实现真正的灰度升级/回退

    Kubernetes中的灰度升级概念应为滚动升级,kubernetes将pod”逐个”的更新,但现实中多业务需要的是稳定的灰度,即同一个app,需要有多个版本同时稳定长时间的存在,TAPP解决了此类问题

  4. 可以指定实例id做删除、停止、重启等操作

    对于微服务app来说,由于没有固定id,因此无法对某个实例做操作,而是全部交给了系统去维持足够的实例数

  5. 对每个实例都有生命周期的跟踪

    对于一个实例,由于机器故障发生了迁移、重启等操作,很难跟踪和监控其生命周期。通过TAPP的容器实例标识,获得了实例真正的生命周期的跟踪,对于判断业务和系统是否正常服务具有特别重要的意义。TAPP还可以记录事件,以及各个实例的运行时间,状态,高可用设置等。

TAPP 资源结构

TApp定义了一种用户自定义资源(CRD),TAPP controller是TAPP对应的controller/operator,它通过kube-apiserver监听TApp、Pod相关的事件,根据TApp spec和status进行相应的操作:创建、删除pod等。

  1. // TApp represents a set of pods with consistent identities.
  2. type TApp struct {
  3. metav1.TypeMeta `json:",inline"`
  4. metav1.ObjectMeta `json:"metadata,omitempty"`
  5. // Spec defines the desired identities of pods in this tapp.
  6. Spec TAppSpec `json:"spec,omitempty"`
  7. // Status is the current status of pods in this TApp. This data
  8. // may be out of date by some window of time.
  9. Status TAppStatus `json:"status,omitempty"`
  10. }
  11. // A TAppSpec is the specification of a TApp.
  12. type TAppSpec struct {
  13. // Replicas 指定Template的副本数,尽管共享同一个Template定义,但是每个副本仍有唯一的标识
  14. Replicas int32 `json:"replicas,omitempty"`
  15. // 同Deployment的定义,标签选择器,默认为Pod Template上的标签
  16. Selector *metav1.LabelSelector `json:"selector,omitempty"`
  17. // Template 默认模板,描述将要被初始创建/默认缩放的pod的对象,在TApp中可以被添加到TemplatePool中
  18. Template corev1.PodTemplateSpec `json:"template"`
  19. // TemplatePool 描述不同版本的pod template, template name --> pod Template
  20. TemplatePool map[string]corev1.PodTemplateSpec `json:"templatePool,omitempty"`
  21. // Statuses 用来指定对应pod实例的目标状态,instanceID --> desiredStatus ["Running","Killed"]
  22. Statuses map[string]InstanceStatus `json:"statuses,omitempty"`
  23. // Templates 用来指定运行pod实例所使用的Template,instanceID --> template name
  24. Templates map[string]string `json:"templates,omitempty"`
  25. // UpdateStrategy 定义滚动更新策略
  26. UpdateStrategy TAppUpdateStrategy `json:"updateStrategy,omitempty"`
  27. // ForceDeletePod 定义是否强制删除pod,默认为false
  28. ForceDeletePod bool `json:"forceDeletePod,omitempty"`
  29. // 同Statefulset的定义
  30. VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"`
  31. }
  32. // 滚动更新策略
  33. type TAppUpdateStrategy struct {
  34. // 滚动更新的template name
  35. Template string `json:"template,omitempty"`
  36. // 滚动更新时的最大不可用数, 如果不指定此配置,滚动更新时不限制最大不可用数
  37. MaxUnavailable *int32 `json:"maxUnavailable,omitempty"`
  38. }
  39. // 定义TApp的状态
  40. type TAppStatus struct {
  41. // most recent generation observed by controller.
  42. ObservedGeneration int64 `json:"observedGeneration,omitempty"`
  43. // Replicas 描述副本数
  44. Replicas int32 `json:"replicas"`
  45. // ReadyReplicas 描述Ready副本数
  46. ReadyReplicas int32 `json:"readyReplicas"`
  47. // ScaleSelector 是用于对pod进行查询的标签,它与HPA使用的副本计数匹配
  48. ScaleLabelSelector string `json:"scaleLabelSelector,omitempty"`
  49. // AppStatus 描述当前Tapp运行状态, 包含"Pending","Running","Failed","Succ","Killed"
  50. AppStatus AppStatus `json:"appStatus,omitempty"`
  51. // Statues 描述实例的运行状态 instanceID --> InstanceStatus ["NotCreated","Pending","Running","Updating","PodFailed","PodSucc","Killing","Killed","Failed","Succ","Unknown"]
  52. Statuses map[string]InstanceStatus `json:"statuses,omitempty"`
  53. }

使用示例

本节以一个TApp应用部署,配置,升级,扩容以及杀死删除的操作步骤来说明TApp的使用。

创建TApp应用

创建TApp应用,副本数为3,TApp-controller将根据默认模板创建出的pod

  1. $ cat tapp.yaml
  2. apiVersion: apps.tkestack.io/v1
  3. kind: TApp
  4. metadata:
  5. name: example-tapp
  6. spec:
  7. replicas: 3
  8. template:
  9. metadata:
  10. labels:
  11. app: example-tapp
  12. spec:
  13. containers:
  14. - name: nginx
  15. image: nginx:1.7.9
  16. $ kubect apply -f tapp.yaml

查看TApp应用

  1. $ kubectl get tapp XXX
  2. NAME AGE
  3. example-tapp 20m
  4. $ kubectl descirbe tapp example-tapp
  5. Name: example-tapp
  6. Namespace: default
  7. Labels: app=example-tapp
  8. Annotations: <none>
  9. API Version: apps.tkestack.io/v1
  10. Kind: TApp
  11. ...
  12. Spec:
  13. ...
  14. Status:
  15. App Status: Running
  16. Observed Generation: 2
  17. Ready Replicas: 3
  18. Replicas: 3
  19. Scale Label Selector: app=example-tapp
  20. Statuses:
  21. 0: Running
  22. 1: Running
  23. 2: Running
  24. Events:
  25. Type Reason Age From Message
  26. ---- ------ ---- ---- -------
  27. Normal SuccessfulCreate 12m tapp-controller Instance: example-tapp-1
  28. Normal SuccessfulCreate 12m tapp-controller Instance: example-tapp-0
  29. Normal SuccessfulCreate 12m tapp-controller Instance: example-tapp-2

升级TApp应用

当前3个pod实例运行的镜像版本为nginx:1.7.9,现在要升级其中的一个pod实例的镜像版本为nginx:latest,在spec.templatPools中创建模板,然后在spec.templates中指定模板pod, 指定“1”:“test”表示使用模板test创建pod 1。

如果只更新镜像,Tapp controller将对pod进行原地升级,即仅更新重启对应的容器,否则将按k8s原生方式删除pod并重新创建它们。

  1. apiVersion: apps.tkestack.io/v1
  2. kind: TApp
  3. metadata:
  4. name: example-tapp
  5. spec:
  6. replicas: 3
  7. template:
  8. metadata:
  9. labels:
  10. app: example-tapp
  11. spec:
  12. containers:
  13. - name: nginx
  14. image: nginx:1.7.9
  15. templatePool:
  16. "test":
  17. metadata:
  18. labels:
  19. app: example-tapp
  20. spec:
  21. containers:
  22. - name: nginx
  23. image: nginx:latest
  24. templates:
  25. "1": "test"

操作成功后,查看instanceID为’1’的pod已升级,镜像版本为nginx:latest

  1. # kubectl describe tapp example-tapp
  2. Name: example-tapp
  3. Namespace: default
  4. Labels: app=example-tapp
  5. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  6. {"apiVersion":"apps.tkestack.io/v1","kind":"TApp","metadata":{"annotations":{},"name":"example-tapp","namespace":"default"},"spec":{"repli...
  7. API Version: apps.tkestack.io/v1
  8. Kind: TApp
  9. ...
  10. Spec:
  11. ...
  12. Templates:
  13. 1: test
  14. Update Strategy:
  15. Status:
  16. App Status: Running
  17. Observed Generation: 4
  18. Ready Replicas: 3
  19. Replicas: 3
  20. Scale Label Selector: app=example-tapp
  21. Statuses:
  22. 0: Running
  23. 1: Running
  24. 2: Running
  25. Events:
  26. Type Reason Age From Message
  27. ---- ------ ---- ---- -------
  28. Normal SuccessfulCreate 25m tapp-controller Instance: example-tapp-1
  29. Normal SuccessfulCreate 25m tapp-controller Instance: example-tapp-0
  30. Normal SuccessfulCreate 25m tapp-controller Instance: example-tapp-2
  31. Normal SuccessfulUpdate 10m tapp-controller Instance: example-tapp-1
  32. # kubectl get pod | grep example-tapp
  33. example-tapp-0 1/1 Running 0 27m
  34. example-tapp-1 1/1 Running 1 27m
  35. example-tapp-2 1/1 Running 0 27m
  36. # kubectl get pod example-tapp-1 -o template --template='{{range .spec.containers}}{{.image}}{{end}}'
  37. nginx:latest

上述升级过程可根据实际需求灵活操作,可以指定多个pod的版本,帮助用户实现灵活的应用升级策略 同时可以指定updateStrategy升级策略,保证升级是最大不可用数为1,即保证滚动升级时每次仅更新和重启一个容器或pod

  1. # cat tapp.yaml
  2. apiVersion: apps.tkestack.io/v1
  3. kind: TApp
  4. metadata:
  5. name: example-tapp
  6. spec:
  7. replicas: 3
  8. template:
  9. metadata:
  10. labels:
  11. app: example-tapp
  12. spec:
  13. containers:
  14. - name: nginx
  15. image: nginx:1.7.9
  16. templatePool:
  17. "test":
  18. metadata:
  19. labels:
  20. app: example-tapp
  21. spec:
  22. containers:
  23. - name: nginx
  24. image: nginx:latest
  25. templates:
  26. "1": "test"
  27. "2": "test"
  28. "0": "test"
  29. updateStrategy:
  30. template: test
  31. maxUnavailable: 1
  32. # kubectl apply -f tapp.yaml

杀死指定pod

在spec.statuses中指定pod的状态,tapp-controller根据用户指定的状态控制pod实例,例如,如果spec.statuses为“1”:“killed”,tapp控制器会杀死pod 1。

  1. # cat tapp.yaml
  2. kind: TApp
  3. metadata:
  4. name: example-tapp
  5. spec:
  6. replicas: 3
  7. template:
  8. metadata:
  9. labels:
  10. app: example-tapp
  11. spec:
  12. containers:
  13. - name: nginx
  14. image: nginx:1.7.9
  15. templatePool:
  16. "test":
  17. metadata:
  18. labels:
  19. app: example-tapp
  20. spec:
  21. containers:
  22. - name: nginx
  23. image: nginx:latest
  24. templates:
  25. "1": "test"
  26. "2": "test"
  27. "0": "test"
  28. updateStrategy:
  29. template: test
  30. maxUnavailable: 1
  31. statuses:
  32. "1": "Killed"
  33. # kubectl apply -f tapp.yaml

查看pod状态变为Terminating

  1. # kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. example-tapp-0 1/1 Running 1 59m
  4. example-tapp-1 0/1 Terminating 1 59m
  5. example-tapp-2 1/1 Running 1 59m

扩容TApp应用

如果你想要扩展TApp使用默认的spec.template模板,只需增加spec.replicas的值,否则你需要在spec.templates中指定使用哪个模板。kubectl scale也适用于TApp。

  1. kubectl scale --replicas=3 tapp/example-tapp

删除TApp应用

  1. kubectl delete tapp example-tapp

其它

Tapp还支持其他功能,如HPA、volume templates,它们与k8s中的其它工作负载类型类似。