handler.go 10 KB

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