一篇从应用部署/服务管治/集群配置三个方便来check你的K8S使用姿势是否正确,包含单不限于服务监控检查/资源使用/标签/HPA,VPA/安全策略/RBAC/日志/监控是否为最佳实践的check list。
readiness probe确定容器何时可以接收流量。
Kubelet执行检查并确定应用程序是否可以接收流量。
liveness probe确定何时应重新启动容器。
kubelet执行检查并确定是否应重新启动容器。
就绪性和存活性探针没有默认值,如果您未设置就绪探针,则kubelet会假定该应用程序已准备就绪,可以在容器启动后立即接收流量。
如果应用程序遇到不可恢复的错误,则应使其崩溃,例如:
请注意,您不应发信号通知Liveness探针失败,相反,您应该立即退出该过程,并让kubelet重新启动容器。
Liveness探针旨在在卡住容器时重新启动容器,例如:
如果您的应用程序正在处理无限循环,则无法退出或寻求帮助。
当该进程消耗100%的CPU时,将没有时间回复(其他)Readiness探针检查,并且最终将其从服务中删除。但是,该Pod仍被注册为当前Deployment的活动副本。如果没有Liveness探针,它将保持运行状态,但与服务分离。换句话说,该过程不仅不处理任何请求,而且还消耗资源。
此时应该怎么办:
请注意,您不应该使用Liveness探针来处理应用程序中的致命错误,并要求Kubernetes重新启动应用程序。相反,您应该让应用程序崩溃。
仅在过程无响应的情况下,才应将“活动性”探针用作恢复机制。
当“活力”和“就绪”探针指向相同的端点时,探针的作用会合并在一起。
当应用程序发出信号表明尚未准备就绪或尚待运行时,kubelet会将容器与服务分离并同时将其删除。
您可能会注意到连接断开,因为容器没有足够的时间耗尽当前连接或处理传入的连接。
可以参考:article that discussed graceful shutdown.
如果应用程序连接到数据库,也许你认为如果数据库为就绪就返回一个失败的就绪就ok了,但是事实并非如此,例如您有一个依赖于后端API的前端应用程序。如果该API不稳定(例如由于错误而有时不可用),则就绪探测器将失败,并且前端应用程序中的相关就绪也将失败。这就会导致停机时间。更一般而言,下游依赖项的故障可能会传播到上游的所有应用程序,并最终也降低前端面层
就绪性探针不应该依以下服务:
详细可参考:explore what happens when there’re dependencies in the readiness probes in this essay.
应用启动时,它不应该因为数据库等依赖项尚未就绪就崩溃,而是,应用程序应继续尝试重新连接数据库,直到成功为止。
Kubernetes希望可以以任何顺序启动应用程序组件。当您确保您的应用程序可以重新连接到诸如数据库之类的依赖项时,这样可以大大提升服务的健壮性。
您应该等待现有连接耗尽并停止处理新连接。请注意,当Pod终止时,该Pod的端点将从服务中删除。
但是,可能需要一些时间才能将诸如kube-proxy或Ingress控制器之类的组件通知更改。
详细可参考:handling client requests correctly with Kubernetes.
正确的优雅停止顺序
可以利用工具测试:test that your app gracefully shuts down with this tool: kube-sigterm-test.
可能需要一些时间才能将诸如kube-proxy或Ingress控制器之类的组件通知端点更改。
因此,尽管标记为已终止,但流量仍可能流向Pod。
该应用程序应停止接受所有剩余连接上的新请求,并在耗尽传出队列后将其关闭。
您可能要考虑使用容器生命周期事件,例如 the preStop handler自定义pod删除之前的动作
通过在应用中捕获SIGTERM信号,可以在Pod即将终止时收到通知。
你应该注意:forwarding the signal to the right process in your container.
如果调用方应用未关闭TCP连接(例如使用TCP保持活动状态或连接池),它将连接到一个Pod,而不使用该服务中的其他Pod。
当时当pod删除的时候会发生什么呢,理想状态请求应该使用其他POD,但是,调用方应用程序与即将终止的Pod的连接寿命很长,它将继续使用它。
另一方面,你不应该突然终止一个长链接,相反,您应该先关闭它们,然后再关闭应用程序。
您可以在有关以下内容的文章中阅读有关保持活动连接的信息:gracefully shutting down a Nodejs HTTP server.
pod部署在这些节点上也将丢失
在以下情况下可以删除pod
以上任何情况都可能影响您的应用程序的可用性,并可能导致停机。
您应该避免所有Pod都无法使用并且无法提供实时流量的情况。
永远不要仅运行一个pod
利用控制器Deployment, DaemonSet, ReplicaSet or StatefulSet.来运行pod,不应该将应用运行为自主式pod
可以参考:Running more than one instance your of your Pods guarantees that deleting a single Pod won’t cause downtime.
即使您运行Pod的多个副本,也无法保证丢失节点不会破坏您的服务。
如果你在单一node上运行一个11个副本集的pod,如果这个node故障,这个服务也一样会停机
运行反亲和性在集群中:You should apply anti-affinity rules to your Deployments so that Pods are spread in all the nodes of your cluster.
详细可以参考: inter-pod affinity and anti-affinity
当一个node被打上污点,pod是要被删除并且重新调度
但是如果你的系统压力很大,不能接受丢失50%的pod这时候改怎么办呢,驱逐pod可能会影响你的服务
为了保护部署免受可能同时摧毁多个Pod的意外事件的影响,可以定义Pod中断预算。
想象一下:“ Kubernetes,请确保我的应用始终至少有5个Pod在运行”。
如果最终状态导致该部署的Pod少于5个,Kubernetes将阻止耗尽事件。
官网参考文档: Pod Disruption Budgets.
为了最大化调度程序的效率,您应该与Kubernetes共享详细信息,例如资源利用,工作负载优先级和开销。
资源限制用于限制容器可以使用多少CPU和内存,并使用containerSpec的resources属性设置。调度程序将这些用作度量标准之一,以确定哪个节点最适合当前Pod。根据调度程序,没有内存限制的容器的内存利用率为零。如果可调度在任何节点上的Pod数量不受限制,则会导致资源超额使用,并可能导致节点(和kubelet)崩溃。
同用的可以适用于CPU限制
但是,您是否应该始终设置内存和CPU的限制和要求?
如果您的进程超出内存限制,则该进程将终止。由于CPU是可压缩的资源,因此如果您的容器超出限制,则将限制该过程。
如果您想更深入地研究CPU和内存限制,则应查看以下文章:
请注意,如果不确定什么是正确的CPU或内存限制,则可以在建议模式打开的情况下使用Kubernetes中的Vertical Pod Autoscaler。自动缩放器会分析您的应用并建议限制。
除非你有计算类型的实例类型job
it is recommended to set the request to 1 CPU or below.
CPU以每个时间单位的CPU时间单位来度量。
cpu:1表示每秒1个CPU秒。
如果您有1个线程,则每秒消耗的CPU时间不能超过1秒。
如果您有2个线程,则可以在0.5秒内消耗1个CPU秒。
8个线程可以在0.125秒内消耗1个CPU秒。之后,您的过程将受到限制。
如果不确定您的应用程序的最佳设置是什么,最好不要设置CPU限制。
深入研究可参考:this article digs deeper in CPU requests and limits.
如果您认为可能忘记设置内存和CPU限制,则应考虑使用LimitRange对象为当前名称空间中部署的容器定义标准大小。
可参考:The official documentation about LimitRange
当一个节点进入过量使用状态(即使用过多资源)时,Kubernetes试图驱逐该节点中的某些Pod。Kubernetes根据定义明确的逻辑对Pod进行排名和逐出。
参考: configuring the quality of service for your Pods
你能通过以下tag标记pod
名称,应用程序的名称,例如“用户API”
实例,标识应用程序实例的唯一名称(您可以使用容器图像标签)
版本,应用程序的当前版本(增量计数器)
组件,架构中的组件,例如“ API”或“数据库”
部分,该应用程序所属的更高级别应用程序的名称,例如“支付网关”由…
管理,用于管理应用程序(例如“ kubectl”或“ Helm”)的操作的工具
标签可参考:recommended by the official documentation.
建议不要标记所有资源。
您可以使用以下标签标记Pod:
tag pod通过以下label
日志对于调试问题和监视应用程序活动特别有用。
有两种日志策略,主动方式与被动方式
使用被动方式日志记录的应用程序不需要了解了解日志记录基础结构,而是将消息记录到标准输出中。
主动方式,该应用程序与中间聚合器建立了网络连接,将数据发送到第三方日志记录服务,或直接写入数据库或索引。
最佳实践:the twelve-factor app.
如果希望将日志转换应用于具有非标准日志事件模型的应用程序,则可能需要使用sidecar容器。
使用Sidecar容器,您可以在将日志条目运送到其他地方之前对其进行规范化。
例如,您可能需要先将Apache日志转换为Logstash JSON格式,然后再将其发送到日志基础结构。
但是,如果您可以控制应用程序,则可以从一开始就输出正确的格式。
sidecares启动需要时间,您可以节省为群集中的每个Pod运行额外的容器的时间。
容器具有本地文件系统,您可能会想使用它来持久化数据。
但是,将持久性数据存储在容器的本地文件系统中会阻止包围的Pod进行水平缩放(即通过添加或删除Pod的副本)。
这是因为,通过使用本地文件系统,每个容器都维护自己的“状态”,这意味着Pod副本的状态可能会随时间而变化。从用户的角度来看,这会导致行为不一致(例如,当请求命中一个Pod时,特定的用户信息可用,但当请求命中另一个Pod时,则不可用)。
相反,任何持久性信息都应保存在Pod外部的中央位置。例如,在集群中的PersistentVolume中,或者在集群外部的某些存储服务中甚至更好。
容器具有本地文件系统,您可能会想使用它来持久化数据。
但是,将持久性数据存储在容器的本地文件系统中会阻止包围的Pod进行水平缩放(即通过添加或删除Pod的副本)。
这是因为,通过使用本地文件系统,每个容器都维护自己的“状态”,这意味着Pod副本的状态可能会随时间而变化。从用户的角度来看,这会导致行为不一致(例如,当请求命中一个Pod时,特定的用户信息可用,但当请求命中另一个Pod时,则不可用)。
相反,任何持久性信息都应保存在Pod外部的中央位置。例如,在集群中的PersistentVolume中,或者在集群外部的某些存储服务中甚至更好。
HPA是kubernetes内置的一个特性它能够监控当前的应用和根据当前的使用率自动添加及删除POD副本
通过配置HPA来保障你的应用在任何情况下包括(异常流量峰值)能够保存可用及正常响应
配置HPA自动伸缩你的应用,需要去创建一个HPA的资源对象,该对象定义了监控你应用的什么指标
HPA也能够健康k8s内置的资源指标(POD的CPU/MEM资源使用率)或者自定义指标,对于自定义指标,你需要去收集和暴露这些指标,例如你可用使用Prometheus/Prometheus Adapter
当POD需要更多资源时,VPA能够通过自动的调整你的POD的资源请求和限制,
VPA非常适用于单体应用无法进行横向副本数的扩张
但是目前VPA仍然处于测试阶段,垂直方向调整POD资源需要重启POD
考虑到这些限制,更多的应用在k8s中可用横行扩张,因此不要在生产环境中使用VPA
集群伸缩是区别于(HPA/VPA)的另一种伸缩类型
集群自动伸缩能够通过增加或移除node节点来自动缩放集群的大小
当由于现有一个node节点的资源不足导致pod调度失败时,此刻集群会进行扩增操作,集群会增加一个work node来保证pod能够正常调度,相似的,如果一个worker node资源使用率低,那么集群自动伸缩会先驱逐这个worker node上面的pod,最终去移除此node
对于集群工作负载很高的应用场景下,就去自动伸缩非常有用,集群自动缩分可以让你满足需求高峰,而不会通过过度陪你走工作节点来浪费资源。
对于工作负载不大的应用场景,可以不用去设置集群自动伸缩,因为可能永远都使用不到改规则,如果你的集群工作负载缓慢增长,可以通过监控系统来手动添加worker node
配置应该在应用之外的代码维护,这有一些好处
在Kubernetes中,可以将配置保存在ConfigMaps中,然后可以在将卷作为环境变量传入时将其安装到容器中。
在ConfigMap中仅保存非敏感配置。对于敏感信息(例如凭据),请使用Secret资源。
Secret资源的内容应作为卷挂载到容器中,而不是作为环境变量传递。
这是为了防止秘密值出现在用于启动容器的命令中,该命令可能由不应该访问秘密值的人员检查
您不应允许用户使用比您事先同意的资源更多的资源。
群集管理员可以设置约束,以使用配额和限制范围限制项目中使用的对象数量或计算资源数量。
详细可参考;limit ranges
如果为设置容器资源消耗限制,那么会出现容器资源争抢导致其他容器异常状况发生,
k8s有两个特性来约束资源使用:ResourceQuota 和 LimitRange.
使用LimitRange对象,您可以定义资源请求的默认值和名称空间内单个容器的限制。
在该命名空间内创建的任何容器(未明确指定请求和限制值)都将分配默认值。
你能够在名称空间中通过资源配额限制所有容器资源的总消耗量
定义名称空间的资源配额会限制属于该名称空间的所有容器可以消耗的CPU,内存或存储资源的总量。
您还可以为其他Kubernetes对象设置配额,例如当前名称空间中的Pod数量。
如果您认为有人可以利用您的集群并创建20000 ConfigMap,则可以使用LimitRange来防止这种情况。
遭到破坏的容器
容器使用节点上不允许的资源,例如进程,网络或文件系统
一般来说,应该将Pod的功能限制在最低限度。
例如,您可以使用Kubernetes Pod安全策略来限制:
选择正确的策略依赖于集群原生的特性,可以参考: Kubernetes Pod Security Policy best practices
在一个POD中,容器能够作为特权模式容器运行来不受限制访问主机系统的资源
通常这样是危险的,你应该给有必要的制定容器可以访问的等级,
特权Pod的有效使用案例包括在节点上使用硬件,例如GPU。
可以参考:learn more about security contexts and privileges containers from this article.
容器中使用只读文件系统来强制保障容器的无状态话
这种方式不仅能减轻风险例如热补丁,而且可以避免避免恶意程序在容器内存储或者操作数据的风险。
使用只读文件系统听起来很简单,但是实际还是有一些复杂
如果你需要写日志或者存储一些临时文件在一个临时目录该怎么办呢,可以参考: running containers securely in production.
在容器中运行一个进程与主机上运行一个进程没有太大区别,只不过一些很小的元数据在容器中声明
因此容器中的uid为0的用户与主机上的root用户相同
如果用户设法脱离了以root用户身份在容器中运行的应用程序,则他们可以使用同一root用户获得对主机的访问权限。
配置容器以使用非特权用户是防止特权升级攻击的最佳方法。
详细可以参考:article offers some detailed explanation examples of what happens when you run your containers as root.
Linux功能使进程能够执行许多特权操作,其中只有root用户默认可以执行。
例如,CAP_CHOWN允许进程“对文件UID和GID进行任意更改”。
即使您的进程不是以root身份运行,进程也有可能通过提升特权来使用那些类似root的功能。
最佳实践:
您应该在关闭特权升级的情况下运行容器,以防止使用setuid或setgid二进制文件提升特权。
如果您打算将群集分成较小的块并在名称空间之间进行隔离,则第一条规则无济于事。
想象一下您的集群中的用户是否能够使用集群中的任何其他服务。
现在,想象一下如果集群中的恶意用户要获得对集群的访问权限,他们可以向整个集群发出请求。
要解决此问题,您可以定义如何使用网络策略允许Pods在当前名称空间和跨名称空间中进行通信。
Kubernetes网络策略指定Pod组的访问权限,就像云中的安全组用于控制对VM实例的访问一样。
换句话说,它在Kubernetes集群上运行的Pod之间创建了防火墙。
可参考:Securing Kubernetes Cluster Networking.
该存储库包含Kubernetes网络策略的各种用例和示例YAML文件,以在您的设置中利用。如果你想知道
how to drop/restrict traffic to applications running on Kubernetes
放弃所需的最小权限是一种常见的做法,但是实际操作又如何量化最小权限?
细粒度的策略提供了更高的安全性,但是需要更多的精力来进行管理。
更大范围的授权可以使不必要的API访问服务帐户,但更易于控制。
请注意,默认的ServiceAccount将自动安装到所有Pod的文件系统中。您可能要禁用它并提供更详细的策略。
可以参考:the default ServiceAccount is automatically mounted into the file system of all Pods.
寻找有关如何设置RBAC规则的好的建议是一项挑战。在Kubernetes RBAC的3种现实方法中,您可以找到三种实用场景和有关如何入门的实用建议。
参考;3 realistic approaches to Kubernetes RBAC
需求:
四个要求转化为五个单独的角色:
例如,您可能要避免从公共互联网下载容器,而希望首先批准这些容器。
也许您有一个内部注册表,并且只有此注册表中的映像可以部署在您的群集中。
您如何强制只能在群集中部署受信任的容器?没有针对此的RBAC政策。
您可能要考虑的最常见的自定义策略之一是限制可以在群集中部署的映像。
The following tutorial explains how you can use the Open Policy Agent to restrict not approved images.
用户创建Ingress清单时,可以使用其中的任何主机名。
但是,您可能希望阻止用户多次使用相同的主机名并互相覆盖。Open Policy Agent的官方文档包含有关如何在验证Webhook中检查Ingress资源的教程。
a tutorial on how to check Ingress resources as part of the validation webhook.
用户创建Ingress清单时,可以使用其中的任何主机名。一种
但是,您可能希望阻止用户使用无效的主机名。Open Policy Agent的官方文档包含有关如何在验证Webhook中检查Ingress资源的教程。
最好的选择是将群集与标准参考进行比较。
对于Kubernetes,参考是Internet安全中心(CIS)基准。
Internet安全中心提供了一些准则和基准测试,以确保代码安全的最佳实践。
他们还维护了Kubernetes的基准,您可以download from the official website.
kube-bench是一种工具,用于自动执行CIS Kubernetes基准测试并报告集群中的错误配置。
[INFO] 1 Master Node Security Configuration
[INFO] 1.1 API Server
[WARN] 1.1.1 Ensure that the --anonymous-auth argument is set to false (Not Scored)
[PASS] 1.1.2 Ensure that the --basic-auth-file argument is not set (Scored)
[PASS] 1.1.3 Ensure that the --insecure-allow-any-token argument is not set (Not Scored)
[PASS] 1.1.4 Ensure that the --kubelet-https argument is set to true (Scored)
[PASS] 1.1.5 Ensure that the --insecure-bind-address argument is not set (Scored)
[PASS] 1.1.6 Ensure that the --insecure-port argument is set to 0 (Scored)
[PASS] 1.1.7 Ensure that the --secure-port argument is not set to 0 (Scored)
[FAIL] 1.1.8 Ensure that the --profiling argument is set to false (Scored)
请注意,无法使用kube-bench检查托管集群(例如GKE,EKS和AKS)的主节点。主节点由云提供商控制。
云平台(AWS,Azure,GCE等)通常将本地元数据服务公开给实例。
默认情况下,实例上运行的Pod可以访问这些API,并且可以包含该节点的云凭据或诸如kubelet凭据之类的置备数据。
这些凭据可用于在群集内升级或升级到同一帐户下的其他云服务。
Alpha和Beta Kubernetes功能正在积极开发中,并且可能会存在限制或错误,从而导致安全漏洞。
始终评估Alpha或Beta功能可能提供的价值,以防对您的安全状况造成潜在风险。
如有疑问,请禁用不使用的功能。
kubernetes提供了不同的认证策略
更多详细策略可参考:in the official documentation.
Kubernetes支持各种身份验证方法,包括OpenID Connect(OIDC)。
OpenID Connect允许单点登录(SSO)(例如您的Google身份)连接到Kubernetes集群和其他开发工具。
你不用分别取记忆和管理认证,您可能有多个群集连接到同一OpenID提供程序。
详细可以参考:learn more about the OpenID connect in Kubernetes
服务帐户令牌不应该用于尝试与Kubernetes群集进行交互的最终用户,但对于在Kubernetes上运行的应用程序和工作负载,它们是首选的身份验证策略。
您应该保留30-45天的历史日志。
从那收集日志?
应该收集什么?
应用程序应该登录到标准输出,而不是文件。
每个节点上的守护程序可以从容器运行时收集日志(如果记录到文件,则可能需要每个pod的sidecar容器)。
使用日志聚合工具,例如EFK堆栈(Elasticsearch,Fluentd,Kibana),DataDog,Sumo Logic,Sysdig,GCP Stackdriver,Azure Monitor,AWS CloudWatch。
自己整理了k8s学习笔记,有兴起的可以一快学习交流:https://github.com/redhatxl/awesome-kubernetes-notes
支持国产容器管理平台KubeSphere,为社区尽自己的一份绵薄之力。