handler.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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 CRI-O containers.
  15. package crio
  16. import (
  17. "fmt"
  18. "path"
  19. "path/filepath"
  20. "strconv"
  21. "strings"
  22. "github.com/google/cadvisor/container"
  23. "github.com/google/cadvisor/container/common"
  24. containerlibcontainer "github.com/google/cadvisor/container/libcontainer"
  25. "github.com/google/cadvisor/fs"
  26. info "github.com/google/cadvisor/info/v1"
  27. cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
  28. libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
  29. )
  30. type crioContainerHandler struct {
  31. machineInfoFactory info.MachineInfoFactory
  32. // Absolute path to the cgroup hierarchies of this container.
  33. // (e.g.: "cpu" -> "/sys/fs/cgroup/cpu/test")
  34. cgroupPaths map[string]string
  35. // the CRI-O storage driver
  36. storageDriver storageDriver
  37. fsInfo fs.FsInfo
  38. rootfsStorageDir string
  39. // Metadata associated with the container.
  40. envs map[string]string
  41. labels map[string]string
  42. // TODO
  43. // crio version handling...
  44. // Image name used for this container.
  45. image string
  46. // The network mode of the container
  47. // TODO
  48. // Filesystem handler.
  49. fsHandler common.FsHandler
  50. // The IP address of the container
  51. ipAddress string
  52. includedMetrics container.MetricSet
  53. reference info.ContainerReference
  54. libcontainerHandler *containerlibcontainer.Handler
  55. }
  56. var _ container.ContainerHandler = &crioContainerHandler{}
  57. // newCrioContainerHandler returns a new container.ContainerHandler
  58. func newCrioContainerHandler(
  59. client crioClient,
  60. name string,
  61. machineInfoFactory info.MachineInfoFactory,
  62. fsInfo fs.FsInfo,
  63. storageDriver storageDriver,
  64. storageDir string,
  65. cgroupSubsystems *containerlibcontainer.CgroupSubsystems,
  66. inHostNamespace bool,
  67. metadataEnvs []string,
  68. includedMetrics container.MetricSet,
  69. ) (container.ContainerHandler, error) {
  70. // Create the cgroup paths.
  71. cgroupPaths := common.MakeCgroupPaths(cgroupSubsystems.MountPoints, name)
  72. // Generate the equivalent cgroup manager for this container.
  73. cgroupManager := &cgroupfs.Manager{
  74. Cgroups: &libcontainerconfigs.Cgroup{
  75. Name: name,
  76. },
  77. Paths: cgroupPaths,
  78. }
  79. rootFs := "/"
  80. if !inHostNamespace {
  81. rootFs = "/rootfs"
  82. storageDir = path.Join(rootFs, storageDir)
  83. }
  84. id := ContainerNameToCrioId(name)
  85. cInfo, err := client.ContainerInfo(id)
  86. if err != nil {
  87. return nil, err
  88. }
  89. // passed to fs handler below ...
  90. // XXX: this is using the full container logpath, as constructed by the CRI
  91. // /var/log/pods/<pod_uuid>/container_instance.log
  92. // It's not actually a log dir, as the CRI doesn't have per-container dirs
  93. // under /var/log/pods/<pod_uuid>/
  94. // We can't use /var/log/pods/<pod_uuid>/ to count per-container log usage.
  95. // We use the container log file directly.
  96. storageLogDir := cInfo.LogPath
  97. // Determine the rootfs storage dir
  98. rootfsStorageDir := cInfo.Root
  99. // TODO(runcom): CRI-O doesn't strip /merged but we need to in order to
  100. // get device ID from root, otherwise, it's going to error out as overlay
  101. // mounts doesn't have fixed dev ids.
  102. rootfsStorageDir = strings.TrimSuffix(rootfsStorageDir, "/merged")
  103. switch storageDriver {
  104. case overlayStorageDriver, overlay2StorageDriver:
  105. // overlay and overlay2 driver are the same "overlay2" driver so treat
  106. // them the same.
  107. rootfsStorageDir = filepath.Join(rootfsStorageDir, "diff")
  108. }
  109. containerReference := info.ContainerReference{
  110. Id: id,
  111. Name: name,
  112. Aliases: []string{cInfo.Name, id},
  113. Namespace: CrioNamespace,
  114. }
  115. libcontainerHandler := containerlibcontainer.NewHandler(cgroupManager, rootFs, cInfo.Pid, includedMetrics)
  116. // TODO: extract object mother method
  117. handler := &crioContainerHandler{
  118. machineInfoFactory: machineInfoFactory,
  119. cgroupPaths: cgroupPaths,
  120. storageDriver: storageDriver,
  121. fsInfo: fsInfo,
  122. rootfsStorageDir: rootfsStorageDir,
  123. envs: make(map[string]string),
  124. labels: cInfo.Labels,
  125. includedMetrics: includedMetrics,
  126. reference: containerReference,
  127. libcontainerHandler: libcontainerHandler,
  128. }
  129. handler.image = cInfo.Image
  130. // TODO: we wantd to know graph driver DeviceId (dont think this is needed now)
  131. // ignore err and get zero as default, this happens with sandboxes, not sure why...
  132. // kube isn't sending restart count in labels for sandboxes.
  133. restartCount, _ := strconv.Atoi(cInfo.Annotations["io.kubernetes.container.restartCount"])
  134. // Only adds restartcount label if it's greater than 0
  135. if restartCount > 0 {
  136. handler.labels["restartcount"] = strconv.Itoa(restartCount)
  137. }
  138. handler.ipAddress = cInfo.IP
  139. // we optionally collect disk usage metrics
  140. if includedMetrics.Has(container.DiskUsageMetrics) {
  141. handler.fsHandler = common.NewFsHandler(common.DefaultPeriod, rootfsStorageDir, storageLogDir, fsInfo)
  142. }
  143. // TODO for env vars we wanted to show from container.Config.Env from whitelist
  144. //for _, exposedEnv := range metadataEnvs {
  145. //klog.V(4).Infof("TODO env whitelist: %v", exposedEnv)
  146. //}
  147. return handler, nil
  148. }
  149. func (self *crioContainerHandler) Start() {
  150. if self.fsHandler != nil {
  151. self.fsHandler.Start()
  152. }
  153. }
  154. func (self *crioContainerHandler) Cleanup() {
  155. if self.fsHandler != nil {
  156. self.fsHandler.Stop()
  157. }
  158. }
  159. func (self *crioContainerHandler) ContainerReference() (info.ContainerReference, error) {
  160. return self.reference, nil
  161. }
  162. func (self *crioContainerHandler) needNet() bool {
  163. if self.includedMetrics.Has(container.NetworkUsageMetrics) {
  164. return self.labels["io.kubernetes.container.name"] == "POD"
  165. }
  166. return false
  167. }
  168. func (self *crioContainerHandler) GetSpec() (info.ContainerSpec, error) {
  169. hasFilesystem := self.includedMetrics.Has(container.DiskUsageMetrics)
  170. spec, err := common.GetSpec(self.cgroupPaths, self.machineInfoFactory, self.needNet(), hasFilesystem)
  171. spec.Labels = self.labels
  172. spec.Envs = self.envs
  173. spec.Image = self.image
  174. return spec, err
  175. }
  176. func (self *crioContainerHandler) getFsStats(stats *info.ContainerStats) error {
  177. mi, err := self.machineInfoFactory.GetMachineInfo()
  178. if err != nil {
  179. return err
  180. }
  181. if self.includedMetrics.Has(container.DiskIOMetrics) {
  182. common.AssignDeviceNamesToDiskStats((*common.MachineInfoNamer)(mi), &stats.DiskIo)
  183. }
  184. if !self.includedMetrics.Has(container.DiskUsageMetrics) {
  185. return nil
  186. }
  187. var device string
  188. switch self.storageDriver {
  189. case overlay2StorageDriver, overlayStorageDriver:
  190. deviceInfo, err := self.fsInfo.GetDirFsDevice(self.rootfsStorageDir)
  191. if err != nil {
  192. return fmt.Errorf("unable to determine device info for dir: %v: %v", self.rootfsStorageDir, err)
  193. }
  194. device = deviceInfo.Device
  195. default:
  196. return nil
  197. }
  198. var (
  199. limit uint64
  200. fsType string
  201. )
  202. // crio does not impose any filesystem limits for containers. So use capacity as limit.
  203. for _, fs := range mi.Filesystems {
  204. if fs.Device == device {
  205. limit = fs.Capacity
  206. fsType = fs.Type
  207. break
  208. }
  209. }
  210. fsStat := info.FsStats{Device: device, Type: fsType, Limit: limit}
  211. usage := self.fsHandler.Usage()
  212. fsStat.BaseUsage = usage.BaseUsageBytes
  213. fsStat.Usage = usage.TotalUsageBytes
  214. fsStat.Inodes = usage.InodeUsage
  215. stats.Filesystem = append(stats.Filesystem, fsStat)
  216. return nil
  217. }
  218. func (self *crioContainerHandler) GetStats() (*info.ContainerStats, error) {
  219. stats, err := self.libcontainerHandler.GetStats()
  220. if err != nil {
  221. return stats, err
  222. }
  223. // Clean up stats for containers that don't have their own network - this
  224. // includes containers running in Kubernetes pods that use the network of the
  225. // infrastructure container. This stops metrics being reported multiple times
  226. // for each container in a pod.
  227. if !self.needNet() {
  228. stats.Network = info.NetworkStats{}
  229. }
  230. // Get filesystem stats.
  231. err = self.getFsStats(stats)
  232. if err != nil {
  233. return stats, err
  234. }
  235. return stats, nil
  236. }
  237. func (self *crioContainerHandler) ListContainers(listType container.ListType) ([]info.ContainerReference, error) {
  238. // No-op for Docker driver.
  239. return []info.ContainerReference{}, nil
  240. }
  241. func (self *crioContainerHandler) GetCgroupPath(resource string) (string, error) {
  242. path, ok := self.cgroupPaths[resource]
  243. if !ok {
  244. return "", fmt.Errorf("could not find path for resource %q for container %q\n", resource, self.reference.Name)
  245. }
  246. return path, nil
  247. }
  248. func (self *crioContainerHandler) GetContainerLabels() map[string]string {
  249. return self.labels
  250. }
  251. func (self *crioContainerHandler) GetContainerIPAddress() string {
  252. return self.ipAddress
  253. }
  254. func (self *crioContainerHandler) ListProcesses(listType container.ListType) ([]int, error) {
  255. return self.libcontainerHandler.GetProcesses()
  256. }
  257. func (self *crioContainerHandler) Exists() bool {
  258. return common.CgroupExists(self.cgroupPaths)
  259. }
  260. func (self *crioContainerHandler) Type() container.ContainerType {
  261. return container.ContainerTypeCrio
  262. }