handler.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. "errors"
  19. "fmt"
  20. "strings"
  21. "time"
  22. "github.com/google/cadvisor/container/containerd/errdefs"
  23. "github.com/opencontainers/runc/libcontainer/cgroups"
  24. "golang.org/x/net/context"
  25. specs "github.com/opencontainers/runtime-spec/specs-go"
  26. "github.com/google/cadvisor/container"
  27. "github.com/google/cadvisor/container/common"
  28. containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
  29. "github.com/google/cadvisor/fs"
  30. info "github.com/google/cadvisor/info/v1"
  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 map[string]string,
  56. inHostNamespace bool,
  57. metadataEnvAllowList []string,
  58. includedMetrics container.MetricSet,
  59. ) (container.ContainerHandler, error) {
  60. // Create the cgroup paths.
  61. cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems, name)
  62. // Generate the equivalent cgroup manager for this container.
  63. cgroupManager, err := containerlibcontainer.NewCgroupManager(name, cgroupPaths)
  64. if err != nil {
  65. return nil, err
  66. }
  67. id := ContainerNameToContainerdID(name)
  68. // We assume that if load fails then the container is not known to containerd.
  69. ctx := context.Background()
  70. cntr, err := client.LoadContainer(ctx, id)
  71. if err != nil {
  72. return nil, err
  73. }
  74. var spec specs.Spec
  75. if err := json.Unmarshal(cntr.Spec.Value, &spec); err != nil {
  76. return nil, err
  77. }
  78. // Cgroup is created during task creation. When cadvisor sees the cgroup,
  79. // task may not be fully created yet. Use a retry+backoff to tolerant the
  80. // race condition.
  81. // TODO(random-liu): Use cri-containerd client to talk with cri-containerd
  82. // instead. cri-containerd has some internal synchronization to make sure
  83. // `ContainerStatus` only returns result after `StartContainer` finishes.
  84. var taskPid uint32
  85. backoff := 100 * time.Millisecond
  86. retry := 5
  87. for {
  88. taskPid, err = client.TaskPid(ctx, id)
  89. if err == nil {
  90. break
  91. }
  92. // Retry when task is not created yet or task is in unknown state (likely in process of initializing)
  93. isRetriableError := errdefs.IsNotFound(err) || errors.Is(err, ErrTaskIsInUnknownState)
  94. if !isRetriableError || retry == 0 {
  95. return nil, err
  96. }
  97. retry--
  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 _, exposedEnv := range metadataEnvAllowList {
  125. if exposedEnv == "" {
  126. // if no containerdEnvWhitelist provided, len(metadataEnvAllowList) == 1, metadataEnvAllowList[0] == ""
  127. continue
  128. }
  129. for _, envVar := range spec.Process.Env {
  130. if envVar != "" {
  131. splits := strings.SplitN(envVar, "=", 2)
  132. if len(splits) == 2 && strings.HasPrefix(splits[0], exposedEnv) {
  133. handler.envs[splits[0]] = splits[1]
  134. }
  135. }
  136. }
  137. }
  138. return handler, nil
  139. }
  140. func (h *containerdContainerHandler) ContainerReference() (info.ContainerReference, error) {
  141. return h.reference, nil
  142. }
  143. func (h *containerdContainerHandler) needNet() bool {
  144. // Since containerd does not handle networking ideally we need to return based
  145. // on includedMetrics list. Here the assumption is the presence of cri-containerd
  146. // label
  147. if h.includedMetrics.Has(container.NetworkUsageMetrics) {
  148. //TODO change it to exported cri-containerd constants
  149. return h.labels["io.cri-containerd.kind"] == "sandbox"
  150. }
  151. return false
  152. }
  153. func (h *containerdContainerHandler) GetSpec() (info.ContainerSpec, error) {
  154. // TODO: Since we dont collect disk usage stats for containerd, we set hasFilesystem
  155. // to false. Revisit when we support disk usage stats for containerd
  156. hasFilesystem := false
  157. spec, err := common.GetSpec(h.cgroupPaths, h.machineInfoFactory, h.needNet(), hasFilesystem)
  158. spec.Labels = h.labels
  159. spec.Envs = h.envs
  160. spec.Image = h.image
  161. return spec, err
  162. }
  163. func (h *containerdContainerHandler) getFsStats(stats *info.ContainerStats) error {
  164. mi, err := h.machineInfoFactory.GetMachineInfo()
  165. if err != nil {
  166. return err
  167. }
  168. if h.includedMetrics.Has(container.DiskIOMetrics) {
  169. common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
  170. }
  171. return nil
  172. }
  173. func (h *containerdContainerHandler) GetStats() (*info.ContainerStats, error) {
  174. stats, err := h.libcontainerHandler.GetStats()
  175. if err != nil {
  176. return stats, err
  177. }
  178. // Clean up stats for containers that don't have their own network - this
  179. // includes containers running in Kubernetes pods that use the network of the
  180. // infrastructure container. This stops metrics being reported multiple times
  181. // for each container in a pod.
  182. if !h.needNet() {
  183. stats.Network = info.NetworkStats{}
  184. }
  185. // Get filesystem stats.
  186. err = h.getFsStats(stats)
  187. return stats, err
  188. }
  189. func (h *containerdContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
  190. return []info.ContainerReference{}, nil
  191. }
  192. func (h *containerdContainerHandler) GetCgroupPath(resource string) (string, error) {
  193. var res string
  194. if !cgroups.IsCgroup2UnifiedMode() {
  195. res = resource
  196. }
  197. path, ok := h.cgroupPaths[res]
  198. if !ok {
  199. return "", fmt.Errorf("could not find path for resource %q for container %q", resource, h.reference.Name)
  200. }
  201. return path, nil
  202. }
  203. func (h *containerdContainerHandler) GetContainerLabels() map[string]string {
  204. return h.labels
  205. }
  206. func (h *containerdContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
  207. return h.libcontainerHandler.GetProcesses()
  208. }
  209. func (h *containerdContainerHandler) Exists() bool {
  210. return common.CgroupExists(h.cgroupPaths)
  211. }
  212. func (h *containerdContainerHandler) Type() container.ContainerType {
  213. return container.ContainerTypeContainerd
  214. }
  215. func (h *containerdContainerHandler) Start() {
  216. }
  217. func (h *containerdContainerHandler) Cleanup() {
  218. }
  219. func (h *containerdContainerHandler) GetContainerIPAddress() string {
  220. // containerd doesnt take care of networking.So it doesnt maintain networking states
  221. return ""
  222. }