如何在 K8s 中部署高可用、运维友好的应用

最近在 K8s 集群中部署一些第三方的应用程序,如 traefik、mariadb-galera 等,为了方便直接用的 helm 官方或者 bitnami 提供的 helm chart,发现这些生产级的 chart 中都会定义一些额外的 K8s 资源来保障应用的高可用性、运维友好以及安全性等,所以写篇文章总结下。

保障高可用性

PodDisruptionBudget

PodDisruptionBudget 是 K8s 中的一个 API 对象,是官方提供的一种 Pod 高可用策略,中文译名为 Pod 中断预算或 Pod 销毁预算,主要保障 多个Pod 副本在外部因素作用下要销毁时,始终保留一部分 Pod 不被销毁。

PDB应对的外部因素包括虚拟机故障、因为资源不够导致的 Pod 驱逐,以及集群管理员的误操作等。

PDB 主要用于那些需要始终保持最小可用性的关键应用程序,始终能够有可用 Pod 来对外提供能力。

例如,在 mariadb-galera 中,PDB的设置如下:

apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: mariadb-galera
  labels: 
     app: mariadb-galera
spec:
  minAvailable: 1

mariadb-galera 的 chart 中默认副本数为 3,而例子中的 minAvailable 参数保证 mariadb 的 Pod 个数至少为1,也就是说即使在外部因素作用下,其他两个副本数因为驱逐或销毁而暂时处于 pending 状态,mariadb-galera 至少还有一个节点可以用于数据库操作,避免了 mariadb 集群短时间处于不可用状态。

HorizontalPodAutoscaler

HorizontalPodAutoscaler 用于 K8s 集群中 Pod 资源的自动水平扩展,主要适用于在系统的负载压力增大或减小的情况下,能够自动地增加或减少后端 Pod 资源来适应动态变化的需求。

HPA 资源最开始只支持对 Pod 的CPU和内存指标的持续检测和对 Pod 资源进行相应地扩缩容,发展到现在已经可以根据支持用户自定义指标来进行扩缩容了,比如对于 Web 应用,可以根据 HTTP 请求数来扩缩容;对于消息队列应用,可以根据队列中消息的堆积情况进行相应地扩缩容。

关于常规指标和自定义指标的采集和 HPA 运行原理,可以参考 Github 项目 k8s-prom-hpa。

在 traefik 的 helm chart 中,有定义如下的 HPA 资源:

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: traefik
  labels:
    app: traefik
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: traefik
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        targetAverageUtilization: 60
    - type: Resource
      resource:
        name: memory
        targetAverageUtilization: 60

一个 HPA 资源的定义主要有三部分组成,操作的资源对象(上述文件中的traefik deployment)、扩缩容的上下限阈值(最少1个副本与最多10个副本)、参考的指标及其预期值(所有关联 Pod 的 CPU 和内存使用率平均值要到60%左右)。在实际应用中,由于 Pod 资源受到所有 Node 节点所能提供的 CPU、内存等资源的制约,往往还要配合 Node 的弹性伸缩来实现。

监控

ServiceMonitor

在 traefik 和 mariadb-galera 的 chart 中都包含了一个 ServiceMonitor 资源,这个 ServiceMonitor 并不是 K8s 的默认资源,它其实是 Prometheus Operator 的一个CRD 组件,用于服务暴露自身指标的。

使用 ServiceMonitor 的好处就是,它可以帮你屏蔽 Prometheus 监控规则配置的复杂性,用户只要提供有限的、简单的信息,Prometheus Operator 会自动把这些信息转化为 Prometheus 复杂的配置规则并应用到 Prometheus 实例中。

应用程序暴露指标的方式可以分为两类,像 traefik 这样的云原生应用,官方已经提供 /metrics 这样的 REST API 接口来暴露自身状态;对于 mariadb 或者 mysql 这样的传统开源软件,开源社区里也可以找到相应的 exporter 来暴露指标。

以 traefik 的 ServiceMonitor 为例:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: traefik
  name: traefik-prometheus-exporter
spec:
  endpoints:
  - port: metrics
    path: /metrics
    interval: 30s
  jobLabel: traefik-prometheus-exporter
  namespaceSelector: {}
  selector:
    matchLabels:
      app: traefik

ServiceMonitor实际上是跟一组 Pod 相关联的,当监控 K8s 中的某个服务时,ServiceMonitor的标签筛选应该与 Service 的标签筛选一致。namespaceSelector 字段表明关联 Pod 的范围,置空时默认只会关联与 ServiceMonitor 同一个 namespace 下的 Pod。

探针

K8s 的探针本质上是由 kubelet 发起的、对容器的一次访问,以获取与容器状态相关的信息。探针分为两种,livenessprobe 和 readinessprobe,前者的作用是判断容器是否需要“存活”,如果检查不通过 kubelet 会重启容器,后者用于判断容器是否可以接受外部访问,如果检查不通过 kubelet 会禁止流量路由到该容器。

在实际运用中,探针的使用场景也很多。比如新加某个 Pod 时,由于某些原因不能正常提供服务,如果不想要 Service 将流量路由给该 Pod,就可以给 Pod 中的主要容器配置探针,Service不会把流量导向健康检查失败的后端 Pod。

在 mariadb-galera 的 statefulset中,mariadb容器也配置了两个探针:

livenessProbe:
            exec:
              command:
                - bash
                - -ec
                - |
                  exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD
            initialDelaySeconds: 120
            periodSeconds: 5
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3
          readinessProbe:
            exec:
              command:
                - bash
                - -ec
                - |
                  exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD
            initialDelaySeconds: 30
            periodSeconds: 5
            timeoutSeconds: 1
            successThreshold: 1
            failureThreshold: 3

这两个探针都是用命令行的方式来检查 mariadb 的状态是否可用,因为 mariadb galera 集群在容器启动后还有一些初始化工作,如果集群未初始化好而同时有新节点加入,就会导致集群创建失败。因此在这个例子中探针的存在非常有必要,它通过健康检查判断出第一个节点是否初始化好,检查通过才会放开后续节点的加入。

安全性

RBAC

RBAC 即 Role Based Access Control,基于角色的访问权限控制,是授权层面上的安全策略,遵循最小权限原则。

K8s 中的 RBAC 主要通过三种资源来实现,ServiceAccount,Role 和 RoleBinding,其中 Role 和 RoleBinding 都是命名空间内有效的,它们的升级版 ClusterRole 和 ClusterRoleBinding 是集群范围有效的。用户(ServiceAccount)和角色(Role)之间是多对多的关系,通过 RoleBinding 表示它们的映射关系。

例如在 mariadb-galera 中,该服务的 SerivceAccount 就只有对 endpoint 资源的读取权限。

使用 Secret

在 K8s 中可以通过 Secret 资源存储小片的敏感数据,Secret由三种类型:

  • Opaque —— 对数据进行 base64 编码,安全性较弱
  • kubernetes.io/dockerconfigjson —— 用于存储访问容器镜像仓库时的认证信息
  • kubernetes.io/service-account-token —— 用户创建 ServiceAccount 时会自动挂载该类型的 Secret 到 Pod 的特定目录下

最常用的还是 Opaque 类型的 Secret,在 mariadb-galera 的 chart 中,数据库密码都用这种方式存储。

Pod粒度的网络访问控制——NetworkPolicy

在传统的服务架构中,网络访问控制策略都是在主机级别上应用的,比如防火墙,或者云服务商提供的安全组是对一组主机进行访问控制;然而在 K8s 集群中,一台主机上可能由提供各种服务的 Pod,而且 Pod 会不断地被调度到不同主机上,原来的基于主机的网络访问控制已经不适应这种变化了,而 K8s 中的 NetworkPolicy 资源正是用于实现 Pod 粒度的网络访问控制。

NetworkPolicy 资源不是 K8s 默认实现的,需要网络插件的支持,比如 Calico 就支持对 NetworkPolicy 的实现。NetworkPolicy 不仅像安全组规则一样支持对出栈和入栈流量,以及基于源IP范围的访问控制,还支持基于 Pod 标签的控制策略。

在 prometheus-operator 中,可以设置如下策略限制 alertmanager 集群间的访问:

apiVersion: extensions/v1beta1
kind: NetworkPolicy
metadata:
  name: alertmanager-mesh
spec:
  ingress:
  - from:
    - podSelector:
        matchExpressions:
        - key: app
          operator: In
          values:
          - alertmanager
        - key: alertmanager
          operator: In
          values:
          - main
    ports:
    - port: 6783
      protocol: tcp
  podSelector:
    matchLabels:
      alertmanager: main
      app: alertmanager

关于 NetworkPolicy 的更详细的说明可以参考 github 中的 kubernetes-network-policy-recipes。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wonain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值