github.com/google/cadvisor@v0.49.1/container/containerd/handler.go (about) 1 // Copyright 2017 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Handler for containerd containers. 16 package containerd 17 18 import ( 19 "encoding/json" 20 "errors" 21 "fmt" 22 "strings" 23 "time" 24 25 "github.com/google/cadvisor/container/containerd/errdefs" 26 "github.com/opencontainers/runc/libcontainer/cgroups" 27 "golang.org/x/net/context" 28 29 specs "github.com/opencontainers/runtime-spec/specs-go" 30 31 "github.com/google/cadvisor/container" 32 "github.com/google/cadvisor/container/common" 33 containerlibcontainer "github.com/google/cadvisor/container/libcontainer" 34 "github.com/google/cadvisor/fs" 35 info "github.com/google/cadvisor/info/v1" 36 ) 37 38 type containerdContainerHandler struct { 39 machineInfoFactory info.MachineInfoFactory 40 // Absolute path to the cgroup hierarchies of this container. 41 // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test") 42 cgroupPaths map[string]string 43 fsInfo fs.FsInfo 44 // Metadata associated with the container. 45 reference info.ContainerReference 46 envs map[string]string 47 labels map[string]string 48 // Image name used for this container. 49 image string 50 // Filesystem handler. 51 includedMetrics container.MetricSet 52 53 libcontainerHandler *containerlibcontainer.Handler 54 } 55 56 var _ container.ContainerHandler = &containerdContainerHandler{} 57 58 // newContainerdContainerHandler returns a new container.ContainerHandler 59 func newContainerdContainerHandler( 60 client ContainerdClient, 61 name string, 62 machineInfoFactory info.MachineInfoFactory, 63 fsInfo fs.FsInfo, 64 cgroupSubsystems map[string]string, 65 inHostNamespace bool, 66 metadataEnvAllowList []string, 67 includedMetrics container.MetricSet, 68 ) (container.ContainerHandler, error) { 69 // Create the cgroup paths. 70 cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name) 71 72 // Generate the equivalent cgroup manager for this container. 73 cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths) 74 if err != nil { 75 return nil, err 76 } 77 78 id := ContainerNameToContainerdID(name) 79 // We assume that if load fails then the container is not known to containerd. 80 ctx := context.Background() 81 cntr, err := client.LoadContainer(ctx, id) 82 if err != nil { 83 return nil, err 84 } 85 86 var spec specs.Spec 87 if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil { 88 return nil, err 89 } 90 91 // Cgroup is created during task creation. When cadvisor sees the cgroup, 92 // task may not be fully created yet. Use a retry+backoff to tolerant the 93 // race condition. 94 // TODO(random-liu): Use cri-containerd client to talk with cri-containerd 95 // instead. cri-containerd has some internal synchronization to make sure 96 // `ContainerStatus` only returns result after `StartContainer` finishes. 97 var taskPid uint32 98 backoff := 100 * time.Millisecond 99 retry := 5 100 for { 101 taskPid, err = client.TaskPid(ctx, id) 102 if err == nil { 103 break 104 } 105 106 // Retry when task is not created yet or task is in unknown state (likely in process of initializing) 107 isRetriableError := errdefs.IsNotFound(err) || errors.Is(err, ErrTaskIsInUnknownState) 108 if !isRetriableError || retry == 0 { 109 return nil, err 110 } 111 112 retry-- 113 time.Sleep(backoff) 114 backoff *= 2 115 } 116 117 rootfs := "/" 118 if !inHostNamespace { 119 rootfs = "/rootfs" 120 } 121 122 containerReference := info.ContainerReference{ 123 Id: id, 124 Name: name, 125 Namespace: k8sContainerdNamespace, 126 Aliases: []string{id, name}, 127 } 128 129 // Containers that don't have their own network -- this includes 130 // containers running in Kubernetes pods that use the network of the 131 // infrastructure container -- does not need their stats to be 132 // reported. This stops metrics being reported multiple times for each 133 // container in a pod. 134 metrics := common.RemoveNetMetrics(includedMetrics, cntr.Labels["io.cri-containerd.kind"] != "sandbox") 135 136 libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), metrics) 137 138 handler := &containerdContainerHandler{ 139 machineInfoFactory: machineInfoFactory, 140 cgroupPaths: cgroupPaths, 141 fsInfo: fsInfo, 142 envs: make(map[string]string), 143 labels: cntr.Labels, 144 includedMetrics: metrics, 145 reference: containerReference, 146 libcontainerHandler: libcontainerHandler, 147 } 148 // Add the name and bare ID as aliases of the container. 149 handler.image = cntr.Image 150 151 for _, exposedEnv := range metadataEnvAllowList { 152 if exposedEnv == "" { 153 // if no containerdEnvWhitelist provided, len(metadataEnvAllowList) == 1, metadataEnvAllowList[0] == "" 154 continue 155 } 156 157 for _, envVar := range spec.Process.Env { 158 if envVar != "" { 159 splits := strings.SplitN(envVar, "=", 2) 160 if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) { 161 handler.envs[splits[0]] = splits[1] 162 } 163 } 164 } 165 } 166 167 return handler, nil 168 } 169 170 func (h *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) { 171 return h.reference, nil 172 } 173 174 func (h *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) { 175 // TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem 176 // to false. Revisit when we support disk usage stats for containerd 177 hasFilesystem := false 178 hasNet := h.includedMetrics.Has(container.NetworkUsageMetrics) 179 spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, hasNet, hasFilesystem) 180 spec.Labels = h.labels 181 spec.Envs = h.envs 182 spec.Image = h.image 183 184 return spec, err 185 } 186 187 func (h *containerdContainerHandler) getFsStats(stats *info.ContainerStats) error { 188 mi, err := h.machineInfoFactory.GetMachineInfo() 189 if err != nil { 190 return err 191 } 192 193 if h.includedMetrics.Has(container.DiskIOMetrics) { 194 common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo) 195 } 196 return nil 197 } 198 199 func (h *containerdContainerHandler) GetStats() (*info.ContainerStats, error) { 200 stats, err := h.libcontainerHandler.GetStats() 201 if err != nil { 202 return stats, err 203 } 204 205 // Get filesystem stats. 206 err = h.getFsStats(stats) 207 return stats, err 208 } 209 210 func (h *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) { 211 return []info.ContainerReference{}, nil 212 } 213 214 func (h *containerdContainerHandler) GetCgroupPath(resource string) (string, error) { 215 var res string 216 if !cgroups.IsCgroup2UnifiedMode() { 217 res = resource 218 } 219 path, ok := h.cgroupPaths[res] 220 if !ok { 221 return "", fmt.Errorf("could not find path for resource %q for container %q", resource, h.reference.Name) 222 } 223 return path, nil 224 } 225 226 func (h *containerdContainerHandler) GetContainerLabels() map[string]string { 227 return h.labels 228 } 229 230 func (h *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) { 231 return h.libcontainerHandler.GetProcesses() 232 } 233 234 func (h *containerdContainerHandler) Exists() bool { 235 return common.CgroupExists(h.cgroupPaths) 236 } 237 238 func (h *containerdContainerHandler) Type() container.ContainerType { 239 return container.ContainerTypeContainerd 240 } 241 242 func (h *containerdContainerHandler) Start() { 243 } 244 245 func (h *containerdContainerHandler) Cleanup() { 246 } 247 248 func (h *containerdContainerHandler) GetContainerIPAddress() string { 249 // containerd doesnt take care of networking.So it doesnt maintain networking states 250 return "" 251 }