Skip to content

Commit

Permalink
cgroup2: implement docker info
Browse files Browse the repository at this point in the history
  • Loading branch information
AkihiroSuda committed Apr 16, 2020
1 parent 2200d93 commit f350b53
Show file tree
Hide file tree
Showing 66 changed files with 7,376 additions and 89 deletions.
7 changes: 7 additions & 0 deletions api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4047,6 +4047,13 @@ definitions:
enum: ["cgroupfs", "systemd", "none"]
default: "cgroupfs"
example: "cgroupfs"
CgroupVersion:
description: |
The version of the cgroup.
type: "string"
enum: ["1", "2"]
default: "1"
example: "1"
NEventsListener:
description: "Number of event listeners subscribed."
type: "integer"
Expand Down
1 change: 1 addition & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ type Info struct {
SystemTime string
LoggingDriver string
CgroupDriver string
CgroupVersion string `json:",omitempty"`
NEventsListener int
KernelVersion string
OperatingSystem string
Expand Down
7 changes: 6 additions & 1 deletion cmd/dockerd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/docker/docker/pkg/pidfile"
"github.com/docker/docker/pkg/plugingetter"
"github.com/docker/docker/pkg/signal"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/plugin"
"github.com/docker/docker/rootless"
Expand Down Expand Up @@ -456,7 +457,11 @@ func warnOnDeprecatedConfigOptions(config *config.Config) {
}

func initRouter(opts routerOptions) {
decoder := runconfig.ContainerDecoder{}
decoder := runconfig.ContainerDecoder{
GetSysInfo: func() *sysinfo.SysInfo {
return opts.daemon.RawSysInfo(true)
},
}

routers := []router.Router{
// we need to add the checkpoint router before the container router or the DELETE gets masked
Expand Down
6 changes: 3 additions & 3 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/docker/docker/errdefs"
bkconfig "github.com/moby/buildkit/cmd/buildkitd/config"
"github.com/moby/buildkit/util/resolver"
rsystem "github.com/opencontainers/runc/libcontainer/system"
"github.com/sirupsen/logrus"

// register graph drivers
Expand All @@ -56,7 +57,6 @@ import (
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/locker"
"github.com/docker/docker/pkg/plugingetter"
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/plugin"
Expand Down Expand Up @@ -1026,10 +1026,10 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
return nil, err
}

sysInfo := sysinfo.New(false)
sysInfo := d.RawSysInfo(false)
// Check if Devices cgroup is mounted, it is hard requirement for container security,
// on Linux.
if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled {
if runtime.GOOS == "linux" && !sysInfo.CgroupDevicesEnabled && !rsystem.RunningInUserNS() {
return nil, errors.New("Devices cgroup isn't mounted")
}

Expand Down
17 changes: 15 additions & 2 deletions daemon/daemon_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
if hostConfig == nil {
return nil, nil
}
sysInfo := sysinfo.New(true)
sysInfo := daemon.RawSysInfo(true)

w, err := verifyPlatformContainerResources(&hostConfig.Resources, sysInfo, update)

Expand Down Expand Up @@ -1745,7 +1745,7 @@ func (daemon *Daemon) initCgroupsPath(path string) error {
}

path = filepath.Join(mnt, root, path)
sysInfo := sysinfo.New(true)
sysInfo := daemon.RawSysInfo(true)
if err := maybeCreateCPURealTimeFile(sysInfo.CPURealtimePeriod, daemon.configStore.CPURealtimePeriod, "cpu.rt_period_us", path); err != nil {
return err
}
Expand Down Expand Up @@ -1779,3 +1779,16 @@ func (daemon *Daemon) setupSeccompProfile() error {
func (daemon *Daemon) useShimV2() bool {
return cgroups.IsCgroup2UnifiedMode()
}

// RawSysInfo returns *sysinfo.SysInfo .
func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
var opts []sysinfo.Opt
if daemon.getCgroupDriver() == cgroupSystemdDriver {
rootlesskitParentEUID := os.Getenv("ROOTLESSKIT_PARENT_EUID")
if rootlesskitParentEUID != "" {
groupPath := fmt.Sprintf("/user.slice/user-%s.slice", rootlesskitParentEUID)
opts = append(opts, sysinfo.WithCgroup2GroupPath(groupPath))
}
}
return sysinfo.New(quiet, opts...)
}
11 changes: 10 additions & 1 deletion daemon/daemon_unsupported.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// +build !linux,!freebsd,!windows

package daemon // import "github.com/docker/docker/daemon"
import "github.com/docker/docker/daemon/config"

import (
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/pkg/sysinfo"
)

const platformSupported = false

func setupResolvConf(config *config.Config) {
}

// RawSysInfo returns *sysinfo.SysInfo .
func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
return sysinfo.New(quiet)
}
5 changes: 5 additions & 0 deletions daemon/daemon_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,3 +657,8 @@ func setupResolvConf(config *config.Config) {
func (daemon *Daemon) useShimV2() bool {
return true
}

// RawSysInfo returns *sysinfo.SysInfo .
func (daemon *Daemon) RawSysInfo(quiet bool) *sysinfo.SysInfo {
return sysinfo.New(quiet)
}
3 changes: 1 addition & 2 deletions daemon/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
func (daemon *Daemon) SystemInfo() *types.Info {
defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()

sysInfo := sysinfo.New(true)
sysInfo := daemon.RawSysInfo(true)
cRunning, cPaused, cStopped := stateCtr.get()

v := &types.Info{
Expand All @@ -47,7 +47,6 @@ func (daemon *Daemon) SystemInfo() *types.Info {
NGoroutines: runtime.NumGoroutine(),
SystemTime: time.Now().Format(time.RFC3339Nano),
LoggingDriver: daemon.defaultLogConfig.Type,
CgroupDriver: daemon.getCgroupDriver(),
NEventsListener: daemon.EventsService.SubscribersCount(),
KernelVersion: kernelVersion(),
OperatingSystem: operatingSystem(),
Expand Down
69 changes: 43 additions & 26 deletions daemon/info_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (

// fillPlatformInfo fills the platform related info.
func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) {
v.CgroupDriver = daemon.getCgroupDriver()
v.CgroupVersion = "1"
if sysInfo.CgroupUnified {
v.CgroupVersion = "2"
}

v.MemoryLimit = sysInfo.MemoryLimit
v.SwapLimit = sysInfo.SwapLimit
v.KernelMemory = sysInfo.KernelMemory
Expand Down Expand Up @@ -81,32 +87,43 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo)
v.InitCommit.ID = "N/A"
}

if !v.MemoryLimit {
v.Warnings = append(v.Warnings, "WARNING: No memory limit support")
}
if !v.SwapLimit {
v.Warnings = append(v.Warnings, "WARNING: No swap limit support")
}
if !v.KernelMemory {
v.Warnings = append(v.Warnings, "WARNING: No kernel memory limit support")
}
if !v.KernelMemoryTCP {
v.Warnings = append(v.Warnings, "WARNING: No kernel memory TCP limit support")
}
if !v.OomKillDisable {
v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support")
}
if !v.CPUCfsQuota {
v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support")
}
if !v.CPUCfsPeriod {
v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support")
}
if !v.CPUShares {
v.Warnings = append(v.Warnings, "WARNING: No cpu shares support")
}
if !v.CPUSet {
v.Warnings = append(v.Warnings, "WARNING: No cpuset support")
if v.CgroupDriver == cgroupNoneDriver {
if v.CgroupVersion == "2" {
v.Warnings = append(v.Warnings, "WARNING: Running in rootless-mode without cgroup. To enable cgroup in rootless-mode, you need to set exec-opt \"native.cgroupdriver=systemd\".")
} else {
v.Warnings = append(v.Warnings, "WARNING: Running in rootless-mode without cgroup. To enable cgroup in rootless-mode, you need to boot the system in cgroup v2 mode and set exec-opt \"native.cgroupdriver=systemd\".")
}
} else {
if !v.MemoryLimit {
v.Warnings = append(v.Warnings, "WARNING: No memory limit support")
}
if !v.SwapLimit {
v.Warnings = append(v.Warnings, "WARNING: No swap limit support")
}
if !v.KernelMemory {
v.Warnings = append(v.Warnings, "WARNING: No kernel memory limit support")
}
if !v.KernelMemoryTCP {
v.Warnings = append(v.Warnings, "WARNING: No kernel memory TCP limit support")
}
if !v.OomKillDisable {
v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support")
}
if !v.CPUCfsQuota {
v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support")
}
if !v.CPUCfsPeriod {
v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support")
}
if !v.CPUShares {
v.Warnings = append(v.Warnings, "WARNING: No cpu shares support")
}
if !v.CPUSet {
v.Warnings = append(v.Warnings, "WARNING: No cpuset support")
}
if v.CgroupVersion == "2" {
v.Warnings = append(v.Warnings, "WARNING: Support for cgroup v2 is experimental")
}
}
if !v.IPv4Forwarding {
v.Warnings = append(v.Warnings, "WARNING: IPv4 forwarding is disabled")
Expand Down
1 change: 1 addition & 0 deletions docs/api/version-history.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ keywords: "API, Docker, rcli, REST, documentation"

[Docker Engine API v1.41](https://docs.docker.com/engine/api/v1.41/) documentation

* `GET /info` now returns an `CgroupVersion` field, containing the cgroup version.
* `POST /services/create` and `POST /services/{id}/update` now supports `BindOptions.NonRecursive`.
* The `ClusterStore` and `ClusterAdvertise` fields in `GET /info` are deprecated
and are now omitted if they contain an empty value. This change is not versioned,
Expand Down
151 changes: 151 additions & 0 deletions pkg/sysinfo/cgroup2_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package sysinfo // import "github.com/docker/docker/pkg/sysinfo"

import (
"io/ioutil"
"path"
"strings"

cgroupsV2 "github.com/containerd/cgroups/v2"
rsystem "github.com/opencontainers/runc/libcontainer/system"
"github.com/sirupsen/logrus"
)

type infoCollectorV2 func(info *SysInfo, controllers map[string]struct{}, dirPath string) (warnings []string)

func newV2(quiet bool, opts *opts) *SysInfo {
var warnings []string
sysInfo := &SysInfo{
CgroupUnified: true,
}
g := opts.cg2GroupPath
if g == "" {
g = "/"
}
m, err := cgroupsV2.LoadManager("/sys/fs/cgroup", g)
if err != nil {
logrus.Warn(err)
} else {
controllersM := make(map[string]struct{})
controllers, err := m.Controllers()
if err != nil {
logrus.Warn(err)
}
for _, c := range controllers {
controllersM[c] = struct{}{}
}
opsV2 := []infoCollectorV2{
applyMemoryCgroupInfoV2,
applyCPUCgroupInfoV2,
applyIOCgroupInfoV2,
applyCPUSetCgroupInfoV2,
applyPIDSCgroupInfoV2,
applyDevicesCgroupInfoV2,
}
dirPath := path.Join("/sys/fs/cgroup", path.Clean(g))
for _, o := range opsV2 {
w := o(sysInfo, controllersM, dirPath)
warnings = append(warnings, w...)
}
}

ops := []infoCollector{
applyNetworkingInfo,
applyAppArmorInfo,
applySeccompInfo,
applyCgroupNsInfo,
}
for _, o := range ops {
w := o(sysInfo, nil)
warnings = append(warnings, w...)
}
if !quiet {
for _, w := range warnings {
logrus.Warn(w)
}
}
return sysInfo
}

func applyMemoryCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ string) []string {
var warnings []string
if _, ok := controllers["memory"]; !ok {
warnings = append(warnings, "Unable to find memory controller")
return warnings
}

info.MemoryLimit = true
info.SwapLimit = true
info.MemoryReservation = true
info.OomKillDisable = false
info.MemorySwappiness = false
info.KernelMemory = false
info.KernelMemoryTCP = false
return warnings
}

func applyCPUCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ string) []string {
var warnings []string
if _, ok := controllers["cpu"]; !ok {
warnings = append(warnings, "Unable to find cpu controller")
return warnings
}
info.CPUShares = true
info.CPUCfsPeriod = true
info.CPUCfsQuota = true
info.CPURealtimePeriod = false
info.CPURealtimeRuntime = false
return warnings
}

func applyIOCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ string) []string {
var warnings []string
if _, ok := controllers["io"]; !ok {
warnings = append(warnings, "Unable to find io controller")
return warnings
}

info.BlkioWeight = true
info.BlkioWeightDevice = true
info.BlkioReadBpsDevice = true
info.BlkioWriteBpsDevice = true
info.BlkioReadIOpsDevice = true
info.BlkioWriteIOpsDevice = true
return warnings
}

func applyCPUSetCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, dirPath string) []string {
var warnings []string
if _, ok := controllers["cpuset"]; !ok {
warnings = append(warnings, "Unable to find cpuset controller")
return warnings
}
info.Cpuset = true

cpus, err := ioutil.ReadFile(path.Join(dirPath, "cpuset.cpus.effective"))
if err != nil {
return warnings
}
info.Cpus = strings.TrimSpace(string(cpus))

mems, err := ioutil.ReadFile(path.Join(dirPath, "cpuset.mems.effective"))
if err != nil {
return warnings
}
info.Mems = strings.TrimSpace(string(mems))
return warnings
}

func applyPIDSCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ string) []string {
var warnings []string
if _, ok := controllers["pids"]; !ok {
warnings = append(warnings, "Unable to find pids controller")
return warnings
}
info.PidsLimit = true
return warnings
}

func applyDevicesCgroupInfoV2(info *SysInfo, controllers map[string]struct{}, _ string) []string {
info.CgroupDevicesEnabled = !rsystem.RunningInUserNS()
return nil
}
Loading

0 comments on commit f350b53

Please sign in to comment.