cgroup_manager_linux.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cm
  14. import (
  15. "fmt"
  16. "os"
  17. "path"
  18. "path/filepath"
  19. "strings"
  20. "time"
  21. units "github.com/docker/go-units"
  22. libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
  23. cgroupfs "github.com/opencontainers/runc/libcontainer/cgroups/fs"
  24. cgroupsystemd "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
  25. libcontainerconfigs "github.com/opencontainers/runc/libcontainer/configs"
  26. "k8s.io/klog"
  27. "k8s.io/apimachinery/pkg/util/sets"
  28. utilfeature "k8s.io/apiserver/pkg/util/feature"
  29. kubefeatures "k8s.io/kubernetes/pkg/features"
  30. "k8s.io/kubernetes/pkg/kubelet/metrics"
  31. )
  32. // libcontainerCgroupManagerType defines how to interface with libcontainer
  33. type libcontainerCgroupManagerType string
  34. const (
  35. // libcontainerCgroupfs means use libcontainer with cgroupfs
  36. libcontainerCgroupfs libcontainerCgroupManagerType = "cgroupfs"
  37. // libcontainerSystemd means use libcontainer with systemd
  38. libcontainerSystemd libcontainerCgroupManagerType = "systemd"
  39. // systemdSuffix is the cgroup name suffix for systemd
  40. systemdSuffix string = ".slice"
  41. )
  42. // hugePageSizeList is useful for converting to the hugetlb canonical unit
  43. // which is what is expected when interacting with libcontainer
  44. var hugePageSizeList = []string{"B", "KB", "MB", "GB", "TB", "PB"}
  45. var RootCgroupName = CgroupName([]string{})
  46. // NewCgroupName composes a new cgroup name.
  47. // Use RootCgroupName as base to start at the root.
  48. // This function does some basic check for invalid characters at the name.
  49. func NewCgroupName(base CgroupName, components ...string) CgroupName {
  50. for _, component := range components {
  51. // Forbit using "_" in internal names. When remapping internal
  52. // names to systemd cgroup driver, we want to remap "-" => "_",
  53. // so we forbid "_" so that we can always reverse the mapping.
  54. if strings.Contains(component, "/") || strings.Contains(component, "_") {
  55. panic(fmt.Errorf("invalid character in component [%q] of CgroupName", component))
  56. }
  57. }
  58. // copy data from the base cgroup to eliminate cases where CgroupNames share underlying slices. See #68416
  59. baseCopy := make([]string, len(base))
  60. copy(baseCopy, base)
  61. return CgroupName(append(baseCopy, components...))
  62. }
  63. func escapeSystemdCgroupName(part string) string {
  64. return strings.Replace(part, "-", "_", -1)
  65. }
  66. func unescapeSystemdCgroupName(part string) string {
  67. return strings.Replace(part, "_", "-", -1)
  68. }
  69. // cgroupName.ToSystemd converts the internal cgroup name to a systemd name.
  70. // For example, the name {"kubepods", "burstable", "pod1234-abcd-5678-efgh"} becomes
  71. // "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1234_abcd_5678_efgh.slice"
  72. // This function always expands the systemd name into the cgroupfs form. If only
  73. // the last part is needed, use path.Base(...) on it to discard the rest.
  74. func (cgroupName CgroupName) ToSystemd() string {
  75. if len(cgroupName) == 0 || (len(cgroupName) == 1 && cgroupName[0] == "") {
  76. return "/"
  77. }
  78. newparts := []string{}
  79. for _, part := range cgroupName {
  80. part = escapeSystemdCgroupName(part)
  81. newparts = append(newparts, part)
  82. }
  83. result, err := cgroupsystemd.ExpandSlice(strings.Join(newparts, "-") + systemdSuffix)
  84. if err != nil {
  85. // Should never happen...
  86. panic(fmt.Errorf("error converting cgroup name [%v] to systemd format: %v", cgroupName, err))
  87. }
  88. return result
  89. }
  90. func ParseSystemdToCgroupName(name string) CgroupName {
  91. driverName := path.Base(name)
  92. driverName = strings.TrimSuffix(driverName, systemdSuffix)
  93. parts := strings.Split(driverName, "-")
  94. result := []string{}
  95. for _, part := range parts {
  96. result = append(result, unescapeSystemdCgroupName(part))
  97. }
  98. return CgroupName(result)
  99. }
  100. func (cgroupName CgroupName) ToCgroupfs() string {
  101. return "/" + path.Join(cgroupName...)
  102. }
  103. func ParseCgroupfsToCgroupName(name string) CgroupName {
  104. components := strings.Split(strings.TrimPrefix(name, "/"), "/")
  105. if len(components) == 1 && components[0] == "" {
  106. components = []string{}
  107. }
  108. return CgroupName(components)
  109. }
  110. func IsSystemdStyleName(name string) bool {
  111. return strings.HasSuffix(name, systemdSuffix)
  112. }
  113. // libcontainerAdapter provides a simplified interface to libcontainer based on libcontainer type.
  114. type libcontainerAdapter struct {
  115. // cgroupManagerType defines how to interface with libcontainer
  116. cgroupManagerType libcontainerCgroupManagerType
  117. }
  118. // newLibcontainerAdapter returns a configured libcontainerAdapter for specified manager.
  119. // it does any initialization required by that manager to function.
  120. func newLibcontainerAdapter(cgroupManagerType libcontainerCgroupManagerType) *libcontainerAdapter {
  121. return &libcontainerAdapter{cgroupManagerType: cgroupManagerType}
  122. }
  123. // newManager returns an implementation of cgroups.Manager
  124. func (l *libcontainerAdapter) newManager(cgroups *libcontainerconfigs.Cgroup, paths map[string]string) (libcontainercgroups.Manager, error) {
  125. switch l.cgroupManagerType {
  126. case libcontainerCgroupfs:
  127. return &cgroupfs.Manager{
  128. Cgroups: cgroups,
  129. Paths: paths,
  130. }, nil
  131. case libcontainerSystemd:
  132. // this means you asked systemd to manage cgroups, but systemd was not on the host, so all you can do is panic...
  133. if !cgroupsystemd.UseSystemd() {
  134. panic("systemd cgroup manager not available")
  135. }
  136. return &cgroupsystemd.Manager{
  137. Cgroups: cgroups,
  138. Paths: paths,
  139. }, nil
  140. }
  141. return nil, fmt.Errorf("invalid cgroup manager configuration")
  142. }
  143. // CgroupSubsystems holds information about the mounted cgroup subsystems
  144. type CgroupSubsystems struct {
  145. // Cgroup subsystem mounts.
  146. // e.g.: "/sys/fs/cgroup/cpu" -> ["cpu", "cpuacct"]
  147. Mounts []libcontainercgroups.Mount
  148. // Cgroup subsystem to their mount location.
  149. // e.g.: "cpu" -> "/sys/fs/cgroup/cpu"
  150. MountPoints map[string]string
  151. }
  152. // cgroupManagerImpl implements the CgroupManager interface.
  153. // Its a stateless object which can be used to
  154. // update,create or delete any number of cgroups
  155. // It uses the Libcontainer raw fs cgroup manager for cgroup management.
  156. type cgroupManagerImpl struct {
  157. // subsystems holds information about all the
  158. // mounted cgroup subsystems on the node
  159. subsystems *CgroupSubsystems
  160. // simplifies interaction with libcontainer and its cgroup managers
  161. adapter *libcontainerAdapter
  162. }
  163. // Make sure that cgroupManagerImpl implements the CgroupManager interface
  164. var _ CgroupManager = &cgroupManagerImpl{}
  165. // NewCgroupManager is a factory method that returns a CgroupManager
  166. func NewCgroupManager(cs *CgroupSubsystems, cgroupDriver string) CgroupManager {
  167. managerType := libcontainerCgroupfs
  168. if cgroupDriver == string(libcontainerSystemd) {
  169. managerType = libcontainerSystemd
  170. }
  171. return &cgroupManagerImpl{
  172. subsystems: cs,
  173. adapter: newLibcontainerAdapter(managerType),
  174. }
  175. }
  176. // Name converts the cgroup to the driver specific value in cgroupfs form.
  177. // This always returns a valid cgroupfs path even when systemd driver is in use!
  178. func (m *cgroupManagerImpl) Name(name CgroupName) string {
  179. if m.adapter.cgroupManagerType == libcontainerSystemd {
  180. return name.ToSystemd()
  181. }
  182. return name.ToCgroupfs()
  183. }
  184. // CgroupName converts the literal cgroupfs name on the host to an internal identifier.
  185. func (m *cgroupManagerImpl) CgroupName(name string) CgroupName {
  186. if m.adapter.cgroupManagerType == libcontainerSystemd {
  187. return ParseSystemdToCgroupName(name)
  188. }
  189. return ParseCgroupfsToCgroupName(name)
  190. }
  191. // buildCgroupPaths builds a path to each cgroup subsystem for the specified name.
  192. func (m *cgroupManagerImpl) buildCgroupPaths(name CgroupName) map[string]string {
  193. cgroupFsAdaptedName := m.Name(name)
  194. cgroupPaths := make(map[string]string, len(m.subsystems.MountPoints))
  195. for key, val := range m.subsystems.MountPoints {
  196. cgroupPaths[key] = path.Join(val, cgroupFsAdaptedName)
  197. }
  198. return cgroupPaths
  199. }
  200. // TODO(filbranden): This logic belongs in libcontainer/cgroup/systemd instead.
  201. // It should take a libcontainerconfigs.Cgroup.Path field (rather than Name and Parent)
  202. // and split it appropriately, using essentially the logic below.
  203. // This was done for cgroupfs in opencontainers/runc#497 but a counterpart
  204. // for systemd was never introduced.
  205. func updateSystemdCgroupInfo(cgroupConfig *libcontainerconfigs.Cgroup, cgroupName CgroupName) {
  206. dir, base := path.Split(cgroupName.ToSystemd())
  207. if dir == "/" {
  208. dir = "-.slice"
  209. } else {
  210. dir = path.Base(dir)
  211. }
  212. cgroupConfig.Parent = dir
  213. cgroupConfig.Name = base
  214. }
  215. // Exists checks if all subsystem cgroups already exist
  216. func (m *cgroupManagerImpl) Exists(name CgroupName) bool {
  217. // Get map of all cgroup paths on the system for the particular cgroup
  218. cgroupPaths := m.buildCgroupPaths(name)
  219. // the presence of alternative control groups not known to runc confuses
  220. // the kubelet existence checks.
  221. // ideally, we would have a mechanism in runc to support Exists() logic
  222. // scoped to the set control groups it understands. this is being discussed
  223. // in https://github.com/opencontainers/runc/issues/1440
  224. // once resolved, we can remove this code.
  225. whitelistControllers := sets.NewString("cpu", "cpuacct", "cpuset", "memory", "systemd")
  226. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  227. whitelistControllers.Insert("pids")
  228. }
  229. var missingPaths []string
  230. // If even one cgroup path doesn't exist, then the cgroup doesn't exist.
  231. for controller, path := range cgroupPaths {
  232. // ignore mounts we don't care about
  233. if !whitelistControllers.Has(controller) {
  234. continue
  235. }
  236. if !libcontainercgroups.PathExists(path) {
  237. missingPaths = append(missingPaths, path)
  238. }
  239. }
  240. if len(missingPaths) > 0 {
  241. klog.V(4).Infof("The Cgroup %v has some missing paths: %v", name, missingPaths)
  242. return false
  243. }
  244. return true
  245. }
  246. // Destroy destroys the specified cgroup
  247. func (m *cgroupManagerImpl) Destroy(cgroupConfig *CgroupConfig) error {
  248. start := time.Now()
  249. defer func() {
  250. metrics.CgroupManagerDuration.WithLabelValues("destroy").Observe(metrics.SinceInSeconds(start))
  251. metrics.DeprecatedCgroupManagerLatency.WithLabelValues("destroy").Observe(metrics.SinceInMicroseconds(start))
  252. }()
  253. cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
  254. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{}
  255. // libcontainer consumes a different field and expects a different syntax
  256. // depending on the cgroup driver in use, so we need this conditional here.
  257. if m.adapter.cgroupManagerType == libcontainerSystemd {
  258. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  259. } else {
  260. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  261. }
  262. manager, err := m.adapter.newManager(libcontainerCgroupConfig, cgroupPaths)
  263. if err != nil {
  264. return err
  265. }
  266. // Delete cgroups using libcontainers Managers Destroy() method
  267. if err = manager.Destroy(); err != nil {
  268. return fmt.Errorf("Unable to destroy cgroup paths for cgroup %v : %v", cgroupConfig.Name, err)
  269. }
  270. return nil
  271. }
  272. type subsystem interface {
  273. // Name returns the name of the subsystem.
  274. Name() string
  275. // Set the cgroup represented by cgroup.
  276. Set(path string, cgroup *libcontainerconfigs.Cgroup) error
  277. // GetStats returns the statistics associated with the cgroup
  278. GetStats(path string, stats *libcontainercgroups.Stats) error
  279. }
  280. // getSupportedSubsystems returns a map of subsystem and if it must be mounted for the kubelet to function.
  281. func getSupportedSubsystems() map[subsystem]bool {
  282. supportedSubsystems := map[subsystem]bool{
  283. &cgroupfs.MemoryGroup{}: true,
  284. &cgroupfs.CpuGroup{}: true,
  285. &cgroupfs.PidsGroup{}: false,
  286. }
  287. // not all hosts support hugetlb cgroup, and in the absent of hugetlb, we will fail silently by reporting no capacity.
  288. supportedSubsystems[&cgroupfs.HugetlbGroup{}] = false
  289. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  290. supportedSubsystems[&cgroupfs.PidsGroup{}] = true
  291. }
  292. return supportedSubsystems
  293. }
  294. // setSupportedSubsystems sets cgroup resource limits only on the supported
  295. // subsystems. ie. cpu and memory. We don't use libcontainer's cgroup/fs/Set()
  296. // method as it doesn't allow us to skip updates on the devices cgroup
  297. // Allowing or denying all devices by writing 'a' to devices.allow or devices.deny is
  298. // not possible once the device cgroups has children. Once the pod level cgroup are
  299. // created under the QOS level cgroup we cannot update the QOS level device cgroup.
  300. // We would like to skip setting any values on the device cgroup in this case
  301. // but this is not possible with libcontainers Set() method
  302. // See https://github.com/opencontainers/runc/issues/932
  303. func setSupportedSubsystems(cgroupConfig *libcontainerconfigs.Cgroup) error {
  304. for sys, required := range getSupportedSubsystems() {
  305. if _, ok := cgroupConfig.Paths[sys.Name()]; !ok {
  306. if required {
  307. return fmt.Errorf("Failed to find subsystem mount for required subsystem: %v", sys.Name())
  308. }
  309. // the cgroup is not mounted, but its not required so continue...
  310. klog.V(6).Infof("Unable to find subsystem mount for optional subsystem: %v", sys.Name())
  311. continue
  312. }
  313. if err := sys.Set(cgroupConfig.Paths[sys.Name()], cgroupConfig); err != nil {
  314. return fmt.Errorf("Failed to set config for supported subsystems : %v", err)
  315. }
  316. }
  317. return nil
  318. }
  319. func (m *cgroupManagerImpl) toResources(resourceConfig *ResourceConfig) *libcontainerconfigs.Resources {
  320. resources := &libcontainerconfigs.Resources{}
  321. if resourceConfig == nil {
  322. return resources
  323. }
  324. if resourceConfig.Memory != nil {
  325. resources.Memory = *resourceConfig.Memory
  326. }
  327. if resourceConfig.CpuShares != nil {
  328. resources.CpuShares = *resourceConfig.CpuShares
  329. }
  330. if resourceConfig.CpuQuota != nil {
  331. resources.CpuQuota = *resourceConfig.CpuQuota
  332. }
  333. if resourceConfig.CpuPeriod != nil {
  334. resources.CpuPeriod = *resourceConfig.CpuPeriod
  335. }
  336. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) || utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportNodePidsLimit) {
  337. if resourceConfig.PidsLimit != nil {
  338. resources.PidsLimit = *resourceConfig.PidsLimit
  339. }
  340. }
  341. // if huge pages are enabled, we set them in libcontainer
  342. // for each page size enumerated, set that value
  343. pageSizes := sets.NewString()
  344. for pageSize, limit := range resourceConfig.HugePageLimit {
  345. sizeString := units.CustomSize("%g%s", float64(pageSize), 1024.0, hugePageSizeList)
  346. resources.HugetlbLimit = append(resources.HugetlbLimit, &libcontainerconfigs.HugepageLimit{
  347. Pagesize: sizeString,
  348. Limit: uint64(limit),
  349. })
  350. pageSizes.Insert(sizeString)
  351. }
  352. // for each page size omitted, limit to 0
  353. for _, pageSize := range cgroupfs.HugePageSizes {
  354. if pageSizes.Has(pageSize) {
  355. continue
  356. }
  357. resources.HugetlbLimit = append(resources.HugetlbLimit, &libcontainerconfigs.HugepageLimit{
  358. Pagesize: pageSize,
  359. Limit: uint64(0),
  360. })
  361. }
  362. return resources
  363. }
  364. // Update updates the cgroup with the specified Cgroup Configuration
  365. func (m *cgroupManagerImpl) Update(cgroupConfig *CgroupConfig) error {
  366. start := time.Now()
  367. defer func() {
  368. metrics.CgroupManagerDuration.WithLabelValues("update").Observe(metrics.SinceInSeconds(start))
  369. metrics.DeprecatedCgroupManagerLatency.WithLabelValues("update").Observe(metrics.SinceInMicroseconds(start))
  370. }()
  371. // Extract the cgroup resource parameters
  372. resourceConfig := cgroupConfig.ResourceParameters
  373. resources := m.toResources(resourceConfig)
  374. cgroupPaths := m.buildCgroupPaths(cgroupConfig.Name)
  375. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
  376. Resources: resources,
  377. Paths: cgroupPaths,
  378. }
  379. // libcontainer consumes a different field and expects a different syntax
  380. // depending on the cgroup driver in use, so we need this conditional here.
  381. if m.adapter.cgroupManagerType == libcontainerSystemd {
  382. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  383. } else {
  384. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  385. }
  386. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
  387. libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
  388. }
  389. if err := setSupportedSubsystems(libcontainerCgroupConfig); err != nil {
  390. return fmt.Errorf("failed to set supported cgroup subsystems for cgroup %v: %v", cgroupConfig.Name, err)
  391. }
  392. return nil
  393. }
  394. // Create creates the specified cgroup
  395. func (m *cgroupManagerImpl) Create(cgroupConfig *CgroupConfig) error {
  396. start := time.Now()
  397. defer func() {
  398. metrics.CgroupManagerDuration.WithLabelValues("create").Observe(metrics.SinceInSeconds(start))
  399. metrics.DeprecatedCgroupManagerLatency.WithLabelValues("create").Observe(metrics.SinceInMicroseconds(start))
  400. }()
  401. resources := m.toResources(cgroupConfig.ResourceParameters)
  402. libcontainerCgroupConfig := &libcontainerconfigs.Cgroup{
  403. Resources: resources,
  404. }
  405. // libcontainer consumes a different field and expects a different syntax
  406. // depending on the cgroup driver in use, so we need this conditional here.
  407. if m.adapter.cgroupManagerType == libcontainerSystemd {
  408. updateSystemdCgroupInfo(libcontainerCgroupConfig, cgroupConfig.Name)
  409. } else {
  410. libcontainerCgroupConfig.Path = cgroupConfig.Name.ToCgroupfs()
  411. }
  412. if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.SupportPodPidsLimit) && cgroupConfig.ResourceParameters != nil && cgroupConfig.ResourceParameters.PidsLimit != nil {
  413. libcontainerCgroupConfig.PidsLimit = *cgroupConfig.ResourceParameters.PidsLimit
  414. }
  415. // get the manager with the specified cgroup configuration
  416. manager, err := m.adapter.newManager(libcontainerCgroupConfig, nil)
  417. if err != nil {
  418. return err
  419. }
  420. // Apply(-1) is a hack to create the cgroup directories for each resource
  421. // subsystem. The function [cgroups.Manager.apply()] applies cgroup
  422. // configuration to the process with the specified pid.
  423. // It creates cgroup files for each subsystems and writes the pid
  424. // in the tasks file. We use the function to create all the required
  425. // cgroup files but not attach any "real" pid to the cgroup.
  426. if err := manager.Apply(-1); err != nil {
  427. return err
  428. }
  429. // it may confuse why we call set after we do apply, but the issue is that runc
  430. // follows a similar pattern. it's needed to ensure cpu quota is set properly.
  431. m.Update(cgroupConfig)
  432. return nil
  433. }
  434. // Scans through all subsystems to find pids associated with specified cgroup.
  435. func (m *cgroupManagerImpl) Pids(name CgroupName) []int {
  436. // we need the driver specific name
  437. cgroupFsName := m.Name(name)
  438. // Get a list of processes that we need to kill
  439. pidsToKill := sets.NewInt()
  440. var pids []int
  441. for _, val := range m.subsystems.MountPoints {
  442. dir := path.Join(val, cgroupFsName)
  443. _, err := os.Stat(dir)
  444. if os.IsNotExist(err) {
  445. // The subsystem pod cgroup is already deleted
  446. // do nothing, continue
  447. continue
  448. }
  449. // Get a list of pids that are still charged to the pod's cgroup
  450. pids, err = getCgroupProcs(dir)
  451. if err != nil {
  452. continue
  453. }
  454. pidsToKill.Insert(pids...)
  455. // WalkFunc which is called for each file and directory in the pod cgroup dir
  456. visitor := func(path string, info os.FileInfo, err error) error {
  457. if err != nil {
  458. klog.V(4).Infof("cgroup manager encountered error scanning cgroup path %q: %v", path, err)
  459. return filepath.SkipDir
  460. }
  461. if !info.IsDir() {
  462. return nil
  463. }
  464. pids, err = getCgroupProcs(path)
  465. if err != nil {
  466. klog.V(4).Infof("cgroup manager encountered error getting procs for cgroup path %q: %v", path, err)
  467. return filepath.SkipDir
  468. }
  469. pidsToKill.Insert(pids...)
  470. return nil
  471. }
  472. // Walk through the pod cgroup directory to check if
  473. // container cgroups haven't been GCed yet. Get attached processes to
  474. // all such unwanted containers under the pod cgroup
  475. if err = filepath.Walk(dir, visitor); err != nil {
  476. klog.V(4).Infof("cgroup manager encountered error scanning pids for directory: %q: %v", dir, err)
  477. }
  478. }
  479. return pidsToKill.List()
  480. }
  481. // ReduceCPULimits reduces the cgroup's cpu shares to the lowest possible value
  482. func (m *cgroupManagerImpl) ReduceCPULimits(cgroupName CgroupName) error {
  483. // Set lowest possible CpuShares value for the cgroup
  484. minimumCPUShares := uint64(MinShares)
  485. resources := &ResourceConfig{
  486. CpuShares: &minimumCPUShares,
  487. }
  488. containerConfig := &CgroupConfig{
  489. Name: cgroupName,
  490. ResourceParameters: resources,
  491. }
  492. return m.Update(containerConfig)
  493. }
  494. func getStatsSupportedSubsystems(cgroupPaths map[string]string) (*libcontainercgroups.Stats, error) {
  495. stats := libcontainercgroups.NewStats()
  496. for sys, required := range getSupportedSubsystems() {
  497. if _, ok := cgroupPaths[sys.Name()]; !ok {
  498. if required {
  499. return nil, fmt.Errorf("Failed to find subsystem mount for required subsystem: %v", sys.Name())
  500. }
  501. // the cgroup is not mounted, but its not required so continue...
  502. klog.V(6).Infof("Unable to find subsystem mount for optional subsystem: %v", sys.Name())
  503. continue
  504. }
  505. if err := sys.GetStats(cgroupPaths[sys.Name()], stats); err != nil {
  506. return nil, fmt.Errorf("Failed to get stats for supported subsystems : %v", err)
  507. }
  508. }
  509. return stats, nil
  510. }
  511. func toResourceStats(stats *libcontainercgroups.Stats) *ResourceStats {
  512. return &ResourceStats{
  513. MemoryStats: &MemoryStats{
  514. Usage: int64(stats.MemoryStats.Usage.Usage),
  515. },
  516. }
  517. }
  518. // Get sets the ResourceParameters of the specified cgroup as read from the cgroup fs
  519. func (m *cgroupManagerImpl) GetResourceStats(name CgroupName) (*ResourceStats, error) {
  520. cgroupPaths := m.buildCgroupPaths(name)
  521. stats, err := getStatsSupportedSubsystems(cgroupPaths)
  522. if err != nil {
  523. return nil, fmt.Errorf("failed to get stats supported cgroup subsystems for cgroup %v: %v", name, err)
  524. }
  525. return toResourceStats(stats), nil
  526. }