123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- // Copyright 2017 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Handler for containerd containers.
- package containerd
- import (
- "encoding/json"
- "fmt"
- "strings"
- "time"
- "github.com/containerd/containerd/errdefs"
- cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
- libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
- "golang.org/x/net/context"
- "github.com/google/cadvisor/container"
- "github.com/google/cadvisor/container/common"
- containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
- "github.com/google/cadvisor/fs"
- info "github.com/google/cadvisor/info/v1"
- specs "github.com/opencontainers/runtime-spec/specs-go"
- )
- type containerdContainerHandler struct {
- machineInfoFactory info.MachineInfoFactory
- // Absolute path to the cgroup hierarchies of this container.
- // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
- cgroupPaths map[string]string
- fsInfo fs.FsInfo
- // Metadata associated with the container.
- reference info.ContainerReference
- envs map[string]string
- labels map[string]string
- // Image name used for this container.
- image string
- // Filesystem handler.
- includedMetrics container.MetricSet
- libcontainerHandler *containerlibcontainer.Handler
- }
- var _ container.ContainerHandler = &containerdContainerHandler{}
- // newContainerdContainerHandler returns a new container.ContainerHandler
- func newContainerdContainerHandler(
- client containerdClient,
- name string,
- machineInfoFactory info.MachineInfoFactory,
- fsInfo fs.FsInfo,
- cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
- inHostNamespace bool,
- metadataEnvs []string,
- includedMetrics container.MetricSet,
- ) (container.ContainerHandler, error) {
- // Create the cgroup paths.
- cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
- // Generate the equivalent cgroup manager for this container.
- cgroupManager := &cgroupfs.Manager{
- Cgroups: &libcontainerconfigs.Cgroup{
- Name: name,
- },
- Paths: cgroupPaths,
- }
- id := ContainerNameToContainerdID(name)
- // We assume that if load fails then the container is not known to containerd.
- ctx := context.Background()
- cntr, err := client.LoadContainer(ctx, id)
- if err != nil {
- return nil, err
- }
- var spec specs.Spec
- if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil {
- return nil, err
- }
- // Cgroup is created during task creation. When cadvisor sees the cgroup,
- // task may not be fully created yet. Use a retry+backoff to tolerant the
- // race condition.
- // TODO(random-liu): Use cri-containerd client to talk with cri-containerd
- // instead. cri-containerd has some internal synchronization to make sure
- // `ContainerStatus` only returns result after `StartContainer` finishes.
- var taskPid uint32
- backoff := 100 * time.Millisecond
- retry := 5
- for {
- taskPid, err = client.TaskPid(ctx, id)
- if err == nil {
- break
- }
- retry--
- if !errdefs.IsNotFound(err) || retry == 0 {
- return nil, err
- }
- time.Sleep(backoff)
- backoff *= 2
- }
- rootfs := "/"
- if !inHostNamespace {
- rootfs = "/rootfs"
- }
- containerReference := info.ContainerReference{
- Id: id,
- Name: name,
- Namespace: k8sContainerdNamespace,
- Aliases: []string{id, name},
- }
- libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
- handler := &containerdContainerHandler{
- machineInfoFactory: machineInfoFactory,
- cgroupPaths: cgroupPaths,
- fsInfo: fsInfo,
- envs: make(map[string]string),
- labels: cntr.Labels,
- includedMetrics: includedMetrics,
- reference: containerReference,
- libcontainerHandler: libcontainerHandler,
- }
- // Add the name and bare ID as aliases of the container.
- handler.image = cntr.Image
- for _, envVar := range spec.Process.Env {
- if envVar != "" {
- splits := strings.SplitN(envVar, "=", 2)
- if len(splits) == 2 {
- handler.envs[splits[0]] = splits[1]
- }
- }
- }
- return handler, nil
- }
- func (self *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) {
- return self.reference, nil
- }
- func (self *containerdContainerHandler) needNet() bool {
- // Since containerd does not handle networking ideally we need to return based
- // on includedMetrics list. Here the assumption is the presence of cri-containerd
- // label
- if self.includedMetrics.Has(container.NetworkUsageMetrics) {
- //TODO change it to exported cri-containerd constants
- return self.labels["io.cri-containerd.kind"] == "sandbox"
- }
- return false
- }
- func (self *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
- // TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
- // to false. Revisit when we support disk usage stats for containerd
- hasFilesystem := false
- spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
- spec.Labels = self.labels
- spec.Envs = self.envs
- spec.Image = self.image
- return spec, err
- }
- func (self *containerdContainerHandler) getFsStats(stats *info.ContainerStats) error {
- mi, err := self.machineInfoFactory.GetMachineInfo()
- if err != nil {
- return err
- }
- if self.includedMetrics.Has(container.DiskIOMetrics) {
- common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
- }
- return nil
- }
- func (self *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
- stats, err := self.libcontainerHandler.GetStats()
- if err != nil {
- return stats, err
- }
- // Clean up stats for containers that don't have their own network - this
- // includes containers running in Kubernetes pods that use the network of the
- // infrastructure container. This stops metrics being reported multiple times
- // for each container in a pod.
- if !self.needNet() {
- stats.Network = info.NetworkStats{}
- }
- // Get filesystem stats.
- err = self.getFsStats(stats)
- return stats, err
- }
- func (self *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
- return []info.ContainerReference{}, nil
- }
- func (self *containerdContainerHandler) GetCgroupPath(resource string) (string, error) {
- path, ok := self.cgroupPaths[resource]
- if !ok {
- return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.reference.Name)
- }
- return path, nil
- }
- func (self *containerdContainerHandler) GetContainerLabels() map[string]string {
- return self.labels
- }
- func (self *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
- return self.libcontainerHandler.GetProcesses()
- }
- func (self *containerdContainerHandler) Exists() bool {
- return common.CgroupExists(self.cgroupPaths)
- }
- func (self *containerdContainerHandler) Type() container.ContainerType {
- return container.ContainerTypeContainerd
- }
- func (self *containerdContainerHandler) Start() {
- }
- func (self *containerdContainerHandler) Cleanup() {
- }
- func (self *containerdContainerHandler) GetContainerIPAddress() string {
- // containerd doesnt take care of networking.So it doesnt maintain networking states
- return ""
- }
|