如何在 K8s 中部署高可用、运维友好的应用
最近在 K8s 集群中部署一些第三方的应用程序,如 traefik、mariadb-galera 等,为了方便直接用的 helm 官方或者 bitnami 提供的 helm chart,发现这些生产级的 chart 中都会定义一些额外的 K8s 资源来保障应用的高可用性、运维友好以及安全性等,所以写篇文章总结下。保障高可用性PodDisruptionBudgetPodDisruptionBu...
最近在 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。
更多推荐
所有评论(0)