kubernetes stdout日志不应计入临时存储使用量,

8ulbf1ek  于 7个月前  发布在  Kubernetes
关注(0)|答案(9)|浏览(87)

发生了什么?

这个问题最初是在 https://discuss.kubernetes.io/t/why-stdout-logs-are-accounted-in-ephemeral-storage-usage/27815 下报告的。
然而,我越想,就越确信这是一个实际的问题。
当 Kubelet 计算临时存储使用情况时,它会将 CRI 管理的日志的使用情况纳入计算:
来自 pkg/kubelet/eviction/eviction_manager.go 的代码片段:

func (m *managerImpl) podEphemeralStorageLimitEviction(podStats statsapi.PodStats, pod *v1.Pod) bool {
	podLimits := resourcehelper.PodLimits(pod, resourcehelper.PodResourcesOptions{})
	_, found := podLimits[v1.ResourceEphemeralStorage]
	if !found {
		return false
	}

	// pod stats api summarizes ephemeral storage usage (container, emptyDir, host[etc-hosts, logs])
	podEphemeralStorageTotalUsage := &resource.Quantity{}
	if podStats.EphemeralStorage != nil && podStats.EphemeralStorage.UsedBytes != nil {
		podEphemeralStorageTotalUsage = resource.NewQuantity(int64(*podStats.EphemeralStorage.UsedBytes), resource.BinarySI)
	}
	podEphemeralStorageLimit := podLimits[v1.ResourceEphemeralStorage]
	if podEphemeralStorageTotalUsage.Cmp(podEphemeralStorageLimit) > 0 {
		// the total usage of pod exceeds the total size limit of containers, evict the pod
		message := fmt.Sprintf(podEphemeralStorageMessageFmt, podEphemeralStorageLimit.String())
		if m.evictPod(pod, 0, message, nil, nil) {
			metrics.Evictions.WithLabelValues(signalEphemeralPodFsLimit).Inc()
			return true
		}
		return false
	}
	return false
}

然而,这些日志消息完全不受容器本身的控制,因为它们在离开容器场所后没有保留/生命周期的影响:相反,这是 Kubelet 的责任,根据 --container-log-max-files 和 --container-log-max-size 设置定义它希望保留这些日志多长时间。
由于这个原因,如果 container-log-max-size * container-log-max-files 大于容器的“ephemeral-storage”限制,那么在容器的整个生命周期中,容器可能会被驱逐(而不会写入任何字节到其挂载的卷中)。

你期望会发生什么?

POD / container 不应该在达到与容器控制下的临时卷相关的容器定义限制之前被驱逐。

我们如何尽可能精确地重现它?

PoC 示例:

---
apiVersion: v1
kind: Pod
metadata:
  name: log-test
spec:
  containers:
  - image: k8s.gcr.io/busybox:latest
    name: test
    command: ["/bin/sh"]
    args:
    - -c
    - |-
      yes $(printf 'Hello world!!!!\n%.0s' `seq 1 64`) | dd bs=1024 count=204800
      while sleep 3600; do
        true
      done
    resources:
      limits:
        ephemeral-storage: "10Mi"

还需要了解其他信息吗?

  • 无响应*

Kubernetes 版本

$ kubectl version --output=json
{
  "clientVersion": {
    "major": "1",
    "minor": "27",
    "gitVersion": "v1.27.6",
    "gitCommit": "741c8db18a52787d734cbe4795f0b4ad860906d6",
    "gitTreeState": "clean",
    "buildDate": "2023-09-13T09:21:34Z",
    "goVersion": "go1.20.8",
    "compiler": "gc",
    "platform": "linux/amd64"
  },
  "kustomizeVersion": "v5.0.1",
  "serverVersion": {
    "major": "1",
    "minor": "28",
    "gitVersion": "v1.28.4",
    "gitCommit": "bae2c62678db2b5053817bc97181fcc2e8388103",
    "gitTreeState": "clean",
    "buildDate": "2023-11-15T16:48:54Z",
    "goVersion": "go1.20.11",
    "compiler": "gc",
    "platform": "linux/amd64"
  }
}

云提供商

N/A

OS 版本

  • 无响应*

安装工具

  • 无响应*

容器运行时(CRI)及其版本(如适用)

  • 无响应*

相关插件(CNI、CSI 等)及其版本(如适用)

n6lpvg4x

n6lpvg4x1#

这个问题目前正在等待分类。
如果SIG或子项目确定这是一个相关的问题,他们将通过应用triage/accepted标签并提供进一步的指导来接受它。
组织成员可以通过在评论中写入/triage accepted来添加triage/accepted标签。
有关使用PR评论与我互动的说明,请查看here。如果您对我的行为有任何问题或建议,请针对kubernetes/test-infra仓库提出一个问题。

k5ifujac

k5ifujac4#

然而,这些日志消息完全不受容器本身的控制,因为它们在离开容器设施后没有保留/生命周期的影响。我个人不同意这一点,因为首先是容器在记录日志。我理解kubelet是选择何时清理日志的实体,但允许容器定义自己的限制功能性地打开节点,使其容易受到来自容器滥用节点并定义其自己任意高的限制的DOS攻击。Kubelet负责保护节点上的其他工作负载,如果容器受到管理员定义的限制,它应该记录更少的日志,我认为。

x9ybnkn6

x9ybnkn65#

你好@haircommander
allowing a container to define its own limits functionally opens the node up for DOS attacks from a container spamming the node and defining its own arbitrarily high limit.
我不确定我们在上下文方面是否保持同步:我不是要求/请求一个功能,让容器定义自己的日志保留——相反,我认为当前的设计(其中kubelet / CRI处理容器产生的日志的生命周期)足以涵盖这一点。
而且因为kubelet已经提供了配置选项来定义每个容器在工作节点上可以消耗的最大日志量,所以我不认为有一种方法可以通过产生大量日志来攻击主机。
默认情况下,配置允许存储50 MiB(未压缩)的日志:
https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/#options :

--container-log-max-files int32     Default: 5
	<Warning: Beta feature> Set the maximum number of container log files that can be present for a container. The number must be >= 2. (DEPRECATED: This parameter should be set via the config file specified by the kubelet's --config flag. See kubelet-config-file for more information.)
--container-log-max-size string     Default: 10Mi
	<Warning: Beta feature> Set the maximum size (e.g. 10Mi) of container log file before it is rotated. (DEPRECATED: This parameter should be set via the config file specified by the kubelet's --config flag. See kubelet-config-file for more information.)

不用说,集群管理员可能会选择以不同的方式配置这些值(允许在工作节点上存储更多或更少的日志,因此每个工作节点允许的最大日志量完全取决于集群管理员的决定。
不,我在这个问题中强调的是相反的情况:如果容器的临时存储限制比kubelet在工作节点上维护的日志量更严格,那么由于计算临时存储使用的方式,容器可能会被驱逐出境。
有人可能会认为容器不应该产生那么多日志,但这只是无法保证容器生命周期内的事情:

  • 即使容器只产生允许日志的一部分,经过很长一段时间,这些小量的日志也会累积起来
  • 有些容器默认就是“健谈”的(即使是INFO日志级别)
  • 其他容器可能需要以调试/跟踪模式运行,在相对较短的时间内可以产生大量日志

示例:

  • ingress-nginx PODs是按设计“健谈”的,它们确实需要频繁旋转(对于该容器,50 MiB的默认值仅足够容纳几个小时的日志):
master-2:~ # crictl ps |grep ingress
49692998d8d53       5aa0bf4798fa2       2 months ago        Running             controller                                   0                   73af5d1f72713       ingress-controller-nginx-ingress-controller-q9zbb
master-2:~ # crictl inspect 49692998d8d53 |grep logPath
	"logPath": "/var/log/pods/ingress-nginx_ingress-controller-nginx-ingress-controller-q9zbb_9d51e25e-22f2-441b-82d2-e4dc6256a70b/controller/0.log",
master-2:~ # ls -l /var/log/pods/ingress-nginx_ingress-controller-nginx-ingress-controller-q9zbb_9d51e25e-22f2-441b-82d2-e4dc6256a70b/controller/
total 18840
-rw-r----- 1 root root  4710921 Apr 25 22:32 0.log
-rw-r--r-- 1 root root  1373597 Apr 25 20:50 0.log.20240425-202651.gz
-rw-r--r-- 1 root root  1372657 Apr 25 21:28 0.log.20240425-205044.gz
-rw-r--r-- 1 root root  1252749 Apr 25 22:12 0.log.20240425-212809.gz
-rw-r----- 1 root root 10560742 Apr 25 22:12 0.log.20240425-221224

// 注意:默认的ingress-nginx / coredns没有附带任何临时存储限制定义,这就是为什么它们不受此问题影响(参见if podStats.EphemeralStorage != nil中的条件podEphemeralStorageLimitEviction())
不幸的是,文档并没有很好地解释“临时存储”限制是如何使用的。
我的原始解释是它限制了容器可以从临时卷中使用的最大的临时存储(假设从2 GiB大小的卷中获得500 MiB的限制),但事实证明这是错误的。
相反,临时存储使用是基于一种容器无法了解的因素进行计算的,因此它甚至无法在清单中概括。
由于这个原因,最好遵循ingress-nginx / coredns的例子,不为临时存储设置任何限制,以避免“神秘的”POD驱逐,但这不就使这个功能变得毫无意义了吗?

fnvucqvd

fnvucqvd6#

将以下文本内容标记为特性,因为集群管理员在规划时需要单独考虑容器日志使用的存储空间。
/kind 特性
/remove-kind 错误

vcirk6k6

vcirk6k67#

Marking this as a feature since the cluster-admin will have to account for storage used by container logs separately while planning.
I would argue that this is already the case: When the cluster admin designs / dimensions a cluster node, it already has to account the number of PODs the system will have to support, and the maximum amount of logs those PODs (and containers related to those PODs) could generate (and thus to configure the --container-log-max-files and --container-log-max-size accordingly, depending on the requirements)
Again - the issue I'm trying to highlight here is the disconnect between the worker node [1] and the container [2] configuration.
[1] kubelet - controls the amount of logs a container could have on a given worker node
[2] manifest - defines the container requirements according to 'Container v1 core', including the resource requests ad limits
Configurations / settings related to these two are completely disconnected from each other, however the POD eviction logic connects them indirectly (by accounting logs that are maintained by kubelet on the worker node).
Because of this I don't see the feature flag justifiable (feels more like a design bug).

vs91vp4v

vs91vp4v8#

我今天在SIG节点会议上提出了这个问题,@SergeyKanzhelev提出了一个很好的观点。将日志的费用计入pod的临时存储的设计可能看起来像是一种反模式,但这是因为完全为pod的所有行为收费是棘手的。我们讨论了推动未来基础设施(kubelet,CRI)上的pod成本与pod数量不是线性关系的目标。这是一个雄心勃勃的目标,因为它们每个pod都有大量的工作要做,但一个好的目标是让基础设施不受pod数量增加的影响,而是让每个pod为其所有行为付费。临时存储帐户如何满足日志的需求实现了这一目标。

wh6knrhe

wh6knrhe9#

你好@haircommander
我认为我们在从不同Angular 考虑这个问题。
我只能从财务Angular 解释“为所有行为充分充电pod”(其中资源使用情况不断被监控并按用户收费 - 类似于亚马逊的做法),但我无法在技术上下文中解释它,在这种情况下,用户缺乏对(她/他)认为可以接受的更改量的控制,并且行为完全由提供者/管理员控制。
关于长期计划:我喜欢这个想法,但在这种情况下,用户是否需要更细粒度的控制来了解自己支付了什么?只要清单没有控制日志保留设置,记录日志数据的收费将完全由提供者/管理员控制。
// 考虑一种情况,提供商因为决定配置工作节点以便存储每个容器的Gigabytes的日志而向你收费(例如:--container-log-max-files 10 --container-log-max-size 1Gi )
当然,还有其他方法来确保客户不会被过度收费(主动资源监控、警报),但Kubernetes本机不提供这些功能,所以我不认为我们可以在这种情况下考虑这些方法。

相关问题