handler.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. // Handler for containerd containers.
  15. package containerd
  16. import (
  17. "encoding/json"
  18. "fmt"
  19. "strings"
  20. "time"
  21. "github.com/containerd/containerd/errdefs"
  22. cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
  23. libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
  24. "golang.org/x/net/context"
  25. "github.com/google/cadvisor/container"
  26. "github.com/google/cadvisor/container/common"
  27. containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
  28. "github.com/google/cadvisor/fs"
  29. info "github.com/google/cadvisor/info/v1"
  30. specs "github.com/opencontainers/runtime-spec/specs-go"
  31. )
  32. type containerdContainerHandler struct {
  33. machineInfoFactory info.MachineInfoFactory
  34. // Absolute path to the cgroup hierarchies of this container.
  35. // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
  36. cgroupPaths map[string]string
  37. fsInfo fs.FsInfo
  38. // Metadata associated with the container.
  39. reference info.ContainerReference
  40. envs map[string]string
  41. labels map[string]string
  42. // Image name used for this container.
  43. image string
  44. // Filesystem handler.
  45. includedMetrics container.MetricSet
  46. libcontainerHandler *containerlibcontainer.Handler
  47. }
  48. var _ container.ContainerHandler = &containerdContainerHandler{}
  49. // newContainerdContainerHandler returns a new container.ContainerHandler
  50. func newContainerdContainerHandler(
  51. client containerdClient,
  52. name string,
  53. machineInfoFactory info.MachineInfoFactory,
  54. fsInfo fs.FsInfo,
  55. cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
  56. inHostNamespace bool,
  57. metadataEnvs []string,
  58. includedMetrics container.MetricSet,
  59. ) (container.ContainerHandler, error) {
  60. // Create the cgroup paths.
  61. cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
  62. // Generate the equivalent cgroup manager for this container.
  63. cgroupManager := &cgroupfs.Manager{
  64. Cgroups: &libcontainerconfigs.Cgroup{
  65. Name: name,
  66. },
  67. Paths: cgroupPaths,
  68. }
  69. id := ContainerNameToContainerdID(name)
  70. // We assume that if load fails then the container is not known to containerd.
  71. ctx := context.Background()
  72. cntr, err := client.LoadContainer(ctx, id)
  73. if err != nil {
  74. return nil, err
  75. }
  76. var spec specs.Spec
  77. if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil {
  78. return nil, err
  79. }
  80. // Cgroup is created during task creation. When cadvisor sees the cgroup,
  81. // task may not be fully created yet. Use a retry+backoff to tolerant the
  82. // race condition.
  83. // TODO(random-liu): Use cri-containerd client to talk with cri-containerd
  84. // instead. cri-containerd has some internal synchronization to make sure
  85. // `ContainerStatus` only returns result after `StartContainer` finishes.
  86. var taskPid uint32
  87. backoff := 100 * time.Millisecond
  88. retry := 5
  89. for {
  90. taskPid, err = client.TaskPid(ctx, id)
  91. if err == nil {
  92. break
  93. }
  94. retry--
  95. if !errdefs.IsNotFound(err) || retry == 0 {
  96. return nil, err
  97. }
  98. time.Sleep(backoff)
  99. backoff *= 2
  100. }
  101. rootfs := "/"
  102. if !inHostNamespace {
  103. rootfs = "/rootfs"
  104. }
  105. containerReference := info.ContainerReference{
  106. Id: id,
  107. Name: name,
  108. Namespace: k8sContainerdNamespace,
  109. Aliases: []string{id, name},
  110. }
  111. libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootfs, int(taskPid), includedMetrics)
  112. handler := &containerdContainerHandler{
  113. machineInfoFactory: machineInfoFactory,
  114. cgroupPaths: cgroupPaths,
  115. fsInfo: fsInfo,
  116. envs: make(map[string]string),
  117. labels: cntr.Labels,
  118. includedMetrics: includedMetrics,
  119. reference: containerReference,
  120. libcontainerHandler: libcontainerHandler,
  121. }
  122. // Add the name and bare ID as aliases of the container.
  123. handler.image = cntr.Image
  124. for _, envVar := range spec.Process.Env {
  125. if envVar != "" {
  126. splits := strings.SplitN(envVar, "=", 2)
  127. if len(splits) == 2 {
  128. handler.envs[splits[0]] = splits[1]
  129. }
  130. }
  131. }
  132. return handler, nil
  133. }
  134. func (self *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) {
  135. return self.reference, nil
  136. }
  137. func (self *containerdContainerHandler) needNet() bool {
  138. // Since containerd does not handle networking ideally we need to return based
  139. // on includedMetrics list. Here the assumption is the presence of cri-containerd
  140. // label
  141. if self.includedMetrics.Has(container.NetworkUsageMetrics) {
  142. //TODO change it to exported cri-containerd constants
  143. return self.labels["io.cri-containerd.kind"] == "sandbox"
  144. }
  145. return false
  146. }
  147. func (self *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
  148. // TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
  149. // to false. Revisit when we support disk usage stats for containerd
  150. hasFilesystem := false
  151. spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
  152. spec.Labels = self.labels
  153. spec.Envs = self.envs
  154. spec.Image = self.image
  155. return spec, err
  156. }
  157. func (self *containerdContainerHandler) getFsStats(stats *info.ContainerStats) error {
  158. mi, err := self.machineInfoFactory.GetMachineInfo()
  159. if err != nil {
  160. return err
  161. }
  162. if self.includedMetrics.Has(container.DiskIOMetrics) {
  163. common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
  164. }
  165. return nil
  166. }
  167. func (self *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
  168. stats, err := self.libcontainerHandler.GetStats()
  169. if err != nil {
  170. return stats, err
  171. }
  172. // Clean up stats for containers that don't have their own network - this
  173. // includes containers running in Kubernetes pods that use the network of the
  174. // infrastructure container. This stops metrics being reported multiple times
  175. // for each container in a pod.
  176. if !self.needNet() {
  177. stats.Network = info.NetworkStats{}
  178. }
  179. // Get filesystem stats.
  180. err = self.getFsStats(stats)
  181. return stats, err
  182. }
  183. func (self *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
  184. return []info.ContainerReference{}, nil
  185. }
  186. func (self *containerdContainerHandler) GetCgroupPath(resource string) (string, error) {
  187. path, ok := self.cgroupPaths[resource]
  188. if !ok {
  189. return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.reference.Name)
  190. }
  191. return path, nil
  192. }
  193. func (self *containerdContainerHandler) GetContainerLabels() map[string]string {
  194. return self.labels
  195. }
  196. func (self *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
  197. return self.libcontainerHandler.GetProcesses()
  198. }
  199. func (self *containerdContainerHandler) Exists() bool {
  200. return common.CgroupExists(self.cgroupPaths)
  201. }
  202. func (self *containerdContainerHandler) Type() container.ContainerType {
  203. return container.ContainerTypeContainerd
  204. }
  205. func (self *containerdContainerHandler) Start() {
  206. }
  207. func (self *containerdContainerHandler) Cleanup() {
  208. }
  209. func (self *containerdContainerHandler) GetContainerIPAddress() string {
  210. // containerd doesnt take care of networking.So it doesnt maintain networking states
  211. return ""
  212. }