I have set up a cache between my kubernetes cluster and the registries it pulls from. I've set it up using the HTTPS_PROXY environment variable, so all fetch requests for images are proxied through the cache. The caching layer is designed for high throughput as I've seen that various registries are quite slow to pull from (eg ECR). The problem is that containerd image fetching appears to throttle layer downloads on a max-bandwidth-per-layer level. Through fetching multiple layers concurrently I can see that it is not (on aggregate) network bound, and disk io tests suggest it is neither that. Some numbers for comparison:
I've tested with various other scenarios: different node types, local and non-local to the caching node, different pulling clients (crictl and nerdctl), as well as creating pods and watching the pull via k8s events. I've tried different storage for containerd root (node-local nvme, different AWS storage classes).
{
"status": {
"conditions": [
{
"type": "RuntimeReady",
"status": true,
"reason": "",
"message": ""
},
{
"type": "NetworkReady",
"status": true,
"reason": "",
"message": ""
}
]
},
"cniconfig": {
"PluginDirs": [
"/opt/cni/bin"
],
"PluginConfDir": "/etc/cni/net.d",
"PluginMaxConfNum": 1,
"Prefix": "eth",
"Networks": [
{
"Config": {
"Name": "cni-loopback",
"CNIVersion": "0.3.1",
"Plugins": [
{
"Network": {
"type": "loopback",
"ipam": {},
"dns": {}
},
"Source": "{\"type\":\"loopback\"}"
}
],
"Source": "{\n\"cniVersion\": \"0.3.1\",\n\"name\": \"cni-loopback\",\n\"plugins\": [{\n \"type\": \"loopback\"\n}]\n}"
},
"IFName": "lo"
},
{
"Config": {
"Name": "aws-cni",
"CNIVersion": "0.3.1",
"Plugins": [
{
"Network": {
"name": "aws-cni",
"type": "aws-cni",
"ipam": {},
"dns": {}
},
"Source": "{\"mtu\":\"9001\",\"name\":\"aws-cni\",\"pluginLogFile\":\"/var/log/aws-routed-eni/plugin.log\",\"pluginLogLevel\":\"DEBUG\",\"type\":\"aws-cni\",\"vethPrefix\":\"eni\"}"
},
{
"Network": {
"name": "egress-v4-cni",
"type": "egress-v4-cni",
"ipam": {
"type": "host-local"
},
"dns": {}
},
"Source": "{\"enabled\":\"false\",\"ipam\":{\"dataDir\":\"/run/cni/v6pd/egress-v4-ipam\",\"ranges\":[[{\"subnet\":\"169.254.172.0/22\"}]],\"routes\":[{\"dst\":\"0.0.0.0/0\"}],\"type\":\"host-local\"},\"mtu\":9001,\"name\":\"egress-v4-cni\",\"nodeIP\":\"192.168.74.5\",\"pluginLogFile\":\"/var/log/aws-routed-eni/egress-v4-plugin.log\",\"pluginLogLevel\":\"DEBUG\",\"type\":\"egress-v4-cni\"}"
},
{
"Network": {
"type": "portmap",
"capabilities": {
"portMappings": true
},
"ipam": {},
"dns": {}
},
"Source": "{\"capabilities\":{\"portMappings\":true},\"snat\":true,\"type\":\"portmap\"}"
},
{
"Network": {
"name": "istio-cni",
"type": "istio-cni",
"ipam": {},
"dns": {}
},
"Source": "{\"kubernetes\":{\"cni_bin_dir\":\"/opt/cni/bin\",\"exclude_namespaces\":[\"istio-system\",\"kube-system\"],\"kubeconfig\":\"/etc/cni/net.d/ZZZ-istio-cni-kubeconfig\"},\"log_level\":\"debug\",\"log_uds_address\":\"/var/run/istio-cni/log.sock\",\"name\":\"istio-cni\",\"type\":\"istio-cni\"}"
}
],
"Source": "{\n \"cniVersion\": \"0.3.1\",\n \"name\": \"aws-cni\",\n \"plugins\": [\n {\n \"mtu\": \"9001\",\n \"name\": \"aws-cni\",\n \"pluginLogFile\": \"/var/log/aws-routed-eni/plugin.log\",\n \"pluginLogLevel\": \"DEBUG\",\n \"type\": \"aws-cni\",\n \"vethPrefix\": \"eni\"\n },\n {\n \"enabled\": \"false\",\n \"ipam\": {\n \"dataDir\": \"/run/cni/v6pd/egress-v4-ipam\",\n \"ranges\": [\n [\n {\n \"subnet\": \"169.254.172.0/22\"\n }\n ]\n ],\n \"routes\": [\n {\n \"dst\": \"0.0.0.0/0\"\n }\n ],\n \"type\": \"host-local\"\n },\n \"mtu\": 9001,\n \"name\": \"egress-v4-cni\",\n \"nodeIP\": \"192.168.74.5\",\n \"pluginLogFile\": \"/var/log/aws-routed-eni/egress-v4-plugin.log\",\n \"pluginLogLevel\": \"DEBUG\",\n \"type\": \"egress-v4-cni\"\n },\n {\n \"capabilities\": {\n \"portMappings\": true\n },\n \"snat\": true,\n \"type\": \"portmap\"\n },\n {\n \"kubernetes\": {\n \"cni_bin_dir\": \"/opt/cni/bin\",\n \"exclude_namespaces\": [\n \"istio-system\",\n \"kube-system\"\n ],\n \"kubeconfig\": \"/etc/cni/net.d/ZZZ-istio-cni-kubeconfig\"\n },\n \"log_level\": \"debug\",\n \"log_uds_address\": \"/var/run/istio-cni/log.sock\",\n \"name\": \"istio-cni\",\n \"type\": \"istio-cni\"\n }\n ]\n}\n"
},
"IFName": "eth0"
}
]
},
"config": {
"containerd": {
"snapshotter": "overlayfs",
"defaultRuntimeName": "runc",
"defaultRuntime": {
"runtimeType": "",
"runtimePath": "",
"runtimeEngine": "",
"PodAnnotations": null,
"ContainerAnnotations": null,
"runtimeRoot": "",
"options": null,
"privileged_without_host_devices": false,
"baseRuntimeSpec": "",
"cniConfDir": "",
"cniMaxConfNum": 0
},
"untrustedWorkloadRuntime": {
"runtimeType": "",
"runtimePath": "",
"runtimeEngine": "",
"PodAnnotations": null,
"ContainerAnnotations": null,
"runtimeRoot": "",
"options": null,
"privileged_without_host_devices": false,
"baseRuntimeSpec": "",
"cniConfDir": "",
"cniMaxConfNum": 0
},
"runtimes": {
"runc": {
"runtimeType": "io.containerd.runc.v2",
"runtimePath": "",
"runtimeEngine": "",
"PodAnnotations": null,
"ContainerAnnotations": null,
"runtimeRoot": "",
"options": {
"SystemdCgroup": true
},
"privileged_without_host_devices": false,
"baseRuntimeSpec": "",
"cniConfDir": "",
"cniMaxConfNum": 0
}
},
"noPivot": false,
"disableSnapshotAnnotations": true,
"discardUnpackedLayers": false,
"ignoreRdtNotEnabledErrors": false
},
"cni": {
"binDir": "/opt/cni/bin",
"confDir": "/etc/cni/net.d",
"maxConfNum": 1,
"confTemplate": "",
"ipPref": ""
},
"registry": {
"configPath": "/etc/containerd/certs.d:/etc/docker/certs.d",
"mirrors": null,
"configs": null,
"auths": null,
"headers": null
},
"imageDecryption": {
"keyModel": "node"
},
"disableTCPService": true,
"streamServerAddress": "127.0.0.1",
"streamServerPort": "0",
"streamIdleTimeout": "4h0m0s",
"enableSelinux": false,
"selinuxCategoryRange": 1024,
"sandboxImage": "602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/pause:3.5",
"statsCollectPeriod": 10,
"systemdCgroup": false,
"enableTLSStreaming": false,
"x509KeyPairStreaming": {
"tlsCertFile": "",
"tlsKeyFile": ""
},
"maxContainerLogSize": 16384,
"disableCgroup": false,
"disableApparmor": false,
"restrictOOMScoreAdj": false,
"maxConcurrentDownloads": 3,
"disableProcMount": false,
"unsetSeccompProfile": "",
"tolerateMissingHugetlbController": true,
"disableHugetlbController": true,
"device_ownership_from_security_context": false,
"ignoreImageDefinedVolumes": false,
"netnsMountsUnderStateDir": false,
"enableUnprivilegedPorts": false,
"enableUnprivilegedICMP": false,
"containerdRootDir": "/mnt/cache/containerd",
"containerdEndpoint": "/run/containerd/containerd.sock",
"rootDir": "/mnt/cache/containerd/io.containerd.grpc.v1.cri",
"stateDir": "/run/containerd/io.containerd.grpc.v1.cri"
},
"golang": "go1.18.6",
"lastCNILoadStatus": "OK",
"lastCNILoadStatus.default": "OK"
}
Description
I have set up a cache between my kubernetes cluster and the registries it pulls from. I've set it up using the HTTPS_PROXY environment variable, so all fetch requests for images are proxied through the cache. The caching layer is designed for high throughput as I've seen that various registries are quite slow to pull from (eg ECR). The problem is that containerd image fetching appears to throttle layer downloads on a max-bandwidth-per-layer level. Through fetching multiple layers concurrently I can see that it is not (on aggregate) network bound, and disk io tests suggest it is neither that. Some numbers for comparison:
curling the cache from the same node: ~2400MB/sI should note that the page caches are cleared between these calls (
sudo sysctl vm.drop_caches=3)Steps to reproduce the issue
Somewhat difficult as it requires setting up a proxy caching server
HTTPS_PROXY=proxy-host ctr --debug -n k8s.io i pull example.com/test-image:image-tagcurlor a attached go module to fetch from cache, note much larger speedup: egtime curl https://proxy-host/v2/test-image/blobs/sha256:<layer_hash>ortime ./fetchbelow.Describe the results you received and expected
Received results
In the below, the layer with
sha256:3ad6e87f8575e89056a7679aa09fc1f7842098633816fce6209e2a3cc008267dis roughly 4GB.fetchis defined byExpected results
Given that the go program attached is in essence what containerd is doing in
func (r dockerFetcher) Fetch(though much simpler and I may very well has missed something as I attempted to sift through the code path), I would expect that the layer download would be approximately the same speed (ie ~2GB/s).Extra info
I've tested with various other scenarios: different node types, local and non-local to the caching node, different pulling clients (
crictlandnerdctl), as well as creating pods and watching the pull via k8s events. I've tried different storage for containerd root (node-local nvme, different AWS storage classes).What version of containerd are you using?
1.6.6 10c1295
Any other relevant information
{ "status": { "conditions": [ { "type": "RuntimeReady", "status": true, "reason": "", "message": "" }, { "type": "NetworkReady", "status": true, "reason": "", "message": "" } ] }, "cniconfig": { "PluginDirs": [ "/opt/cni/bin" ], "PluginConfDir": "/etc/cni/net.d", "PluginMaxConfNum": 1, "Prefix": "eth", "Networks": [ { "Config": { "Name": "cni-loopback", "CNIVersion": "0.3.1", "Plugins": [ { "Network": { "type": "loopback", "ipam": {}, "dns": {} }, "Source": "{\"type\":\"loopback\"}" } ], "Source": "{\n\"cniVersion\": \"0.3.1\",\n\"name\": \"cni-loopback\",\n\"plugins\": [{\n \"type\": \"loopback\"\n}]\n}" }, "IFName": "lo" }, { "Config": { "Name": "aws-cni", "CNIVersion": "0.3.1", "Plugins": [ { "Network": { "name": "aws-cni", "type": "aws-cni", "ipam": {}, "dns": {} }, "Source": "{\"mtu\":\"9001\",\"name\":\"aws-cni\",\"pluginLogFile\":\"/var/log/aws-routed-eni/plugin.log\",\"pluginLogLevel\":\"DEBUG\",\"type\":\"aws-cni\",\"vethPrefix\":\"eni\"}" }, { "Network": { "name": "egress-v4-cni", "type": "egress-v4-cni", "ipam": { "type": "host-local" }, "dns": {} }, "Source": "{\"enabled\":\"false\",\"ipam\":{\"dataDir\":\"/run/cni/v6pd/egress-v4-ipam\",\"ranges\":[[{\"subnet\":\"169.254.172.0/22\"}]],\"routes\":[{\"dst\":\"0.0.0.0/0\"}],\"type\":\"host-local\"},\"mtu\":9001,\"name\":\"egress-v4-cni\",\"nodeIP\":\"192.168.74.5\",\"pluginLogFile\":\"/var/log/aws-routed-eni/egress-v4-plugin.log\",\"pluginLogLevel\":\"DEBUG\",\"type\":\"egress-v4-cni\"}" }, { "Network": { "type": "portmap", "capabilities": { "portMappings": true }, "ipam": {}, "dns": {} }, "Source": "{\"capabilities\":{\"portMappings\":true},\"snat\":true,\"type\":\"portmap\"}" }, { "Network": { "name": "istio-cni", "type": "istio-cni", "ipam": {}, "dns": {} }, "Source": "{\"kubernetes\":{\"cni_bin_dir\":\"/opt/cni/bin\",\"exclude_namespaces\":[\"istio-system\",\"kube-system\"],\"kubeconfig\":\"/etc/cni/net.d/ZZZ-istio-cni-kubeconfig\"},\"log_level\":\"debug\",\"log_uds_address\":\"/var/run/istio-cni/log.sock\",\"name\":\"istio-cni\",\"type\":\"istio-cni\"}" } ], "Source": "{\n \"cniVersion\": \"0.3.1\",\n \"name\": \"aws-cni\",\n \"plugins\": [\n {\n \"mtu\": \"9001\",\n \"name\": \"aws-cni\",\n \"pluginLogFile\": \"/var/log/aws-routed-eni/plugin.log\",\n \"pluginLogLevel\": \"DEBUG\",\n \"type\": \"aws-cni\",\n \"vethPrefix\": \"eni\"\n },\n {\n \"enabled\": \"false\",\n \"ipam\": {\n \"dataDir\": \"/run/cni/v6pd/egress-v4-ipam\",\n \"ranges\": [\n [\n {\n \"subnet\": \"169.254.172.0/22\"\n }\n ]\n ],\n \"routes\": [\n {\n \"dst\": \"0.0.0.0/0\"\n }\n ],\n \"type\": \"host-local\"\n },\n \"mtu\": 9001,\n \"name\": \"egress-v4-cni\",\n \"nodeIP\": \"192.168.74.5\",\n \"pluginLogFile\": \"/var/log/aws-routed-eni/egress-v4-plugin.log\",\n \"pluginLogLevel\": \"DEBUG\",\n \"type\": \"egress-v4-cni\"\n },\n {\n \"capabilities\": {\n \"portMappings\": true\n },\n \"snat\": true,\n \"type\": \"portmap\"\n },\n {\n \"kubernetes\": {\n \"cni_bin_dir\": \"/opt/cni/bin\",\n \"exclude_namespaces\": [\n \"istio-system\",\n \"kube-system\"\n ],\n \"kubeconfig\": \"/etc/cni/net.d/ZZZ-istio-cni-kubeconfig\"\n },\n \"log_level\": \"debug\",\n \"log_uds_address\": \"/var/run/istio-cni/log.sock\",\n \"name\": \"istio-cni\",\n \"type\": \"istio-cni\"\n }\n ]\n}\n" }, "IFName": "eth0" } ] }, "config": { "containerd": { "snapshotter": "overlayfs", "defaultRuntimeName": "runc", "defaultRuntime": { "runtimeType": "", "runtimePath": "", "runtimeEngine": "", "PodAnnotations": null, "ContainerAnnotations": null, "runtimeRoot": "", "options": null, "privileged_without_host_devices": false, "baseRuntimeSpec": "", "cniConfDir": "", "cniMaxConfNum": 0 }, "untrustedWorkloadRuntime": { "runtimeType": "", "runtimePath": "", "runtimeEngine": "", "PodAnnotations": null, "ContainerAnnotations": null, "runtimeRoot": "", "options": null, "privileged_without_host_devices": false, "baseRuntimeSpec": "", "cniConfDir": "", "cniMaxConfNum": 0 }, "runtimes": { "runc": { "runtimeType": "io.containerd.runc.v2", "runtimePath": "", "runtimeEngine": "", "PodAnnotations": null, "ContainerAnnotations": null, "runtimeRoot": "", "options": { "SystemdCgroup": true }, "privileged_without_host_devices": false, "baseRuntimeSpec": "", "cniConfDir": "", "cniMaxConfNum": 0 } }, "noPivot": false, "disableSnapshotAnnotations": true, "discardUnpackedLayers": false, "ignoreRdtNotEnabledErrors": false }, "cni": { "binDir": "/opt/cni/bin", "confDir": "/etc/cni/net.d", "maxConfNum": 1, "confTemplate": "", "ipPref": "" }, "registry": { "configPath": "/etc/containerd/certs.d:/etc/docker/certs.d", "mirrors": null, "configs": null, "auths": null, "headers": null }, "imageDecryption": { "keyModel": "node" }, "disableTCPService": true, "streamServerAddress": "127.0.0.1", "streamServerPort": "0", "streamIdleTimeout": "4h0m0s", "enableSelinux": false, "selinuxCategoryRange": 1024, "sandboxImage": "602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/pause:3.5", "statsCollectPeriod": 10, "systemdCgroup": false, "enableTLSStreaming": false, "x509KeyPairStreaming": { "tlsCertFile": "", "tlsKeyFile": "" }, "maxContainerLogSize": 16384, "disableCgroup": false, "disableApparmor": false, "restrictOOMScoreAdj": false, "maxConcurrentDownloads": 3, "disableProcMount": false, "unsetSeccompProfile": "", "tolerateMissingHugetlbController": true, "disableHugetlbController": true, "device_ownership_from_security_context": false, "ignoreImageDefinedVolumes": false, "netnsMountsUnderStateDir": false, "enableUnprivilegedPorts": false, "enableUnprivilegedICMP": false, "containerdRootDir": "/mnt/cache/containerd", "containerdEndpoint": "/run/containerd/containerd.sock", "rootDir": "/mnt/cache/containerd/io.containerd.grpc.v1.cri", "stateDir": "/run/containerd/io.containerd.grpc.v1.cri" }, "golang": "go1.18.6", "lastCNILoadStatus": "OK", "lastCNILoadStatus.default": "OK" }Show configuration if it is related to CRI plugin.
No response