...

Source file src/github.com/opencontainers/runc/update.go

Documentation: github.com/opencontainers/runc

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"strconv"
     9  
    10  	"github.com/opencontainers/runc/libcontainer/cgroups"
    11  	"github.com/sirupsen/logrus"
    12  
    13  	"github.com/docker/go-units"
    14  	"github.com/opencontainers/runc/libcontainer/configs"
    15  	"github.com/opencontainers/runc/libcontainer/intelrdt"
    16  	"github.com/opencontainers/runtime-spec/specs-go"
    17  	"github.com/urfave/cli"
    18  )
    19  
    20  func i64Ptr(i int64) *int64   { return &i }
    21  func u64Ptr(i uint64) *uint64 { return &i }
    22  func u16Ptr(i uint16) *uint16 { return &i }
    23  
    24  var updateCommand = cli.Command{
    25  	Name:      "update",
    26  	Usage:     "update container resource constraints",
    27  	ArgsUsage: `<container-id>`,
    28  	Flags: []cli.Flag{
    29  		cli.StringFlag{
    30  			Name:  "resources, r",
    31  			Value: "",
    32  			Usage: `path to the file containing the resources to update or '-' to read from the standard input
    33  
    34  The accepted format is as follow (unchanged values can be omitted):
    35  
    36  {
    37    "memory": {
    38      "limit": 0,
    39      "reservation": 0,
    40      "swap": 0
    41    },
    42    "cpu": {
    43      "shares": 0,
    44      "quota": 0,
    45      "period": 0,
    46      "realtimeRuntime": 0,
    47      "realtimePeriod": 0,
    48      "cpus": "",
    49      "mems": ""
    50    },
    51    "blockIO": {
    52      "weight": 0
    53    }
    54  }
    55  
    56  Note: if data is to be read from a file or the standard input, all
    57  other options are ignored.
    58  `,
    59  		},
    60  
    61  		cli.IntFlag{
    62  			Name:  "blkio-weight",
    63  			Usage: "Specifies per cgroup weight, range is from 10 to 1000",
    64  		},
    65  		cli.StringFlag{
    66  			Name:  "cpu-period",
    67  			Usage: "CPU CFS period to be used for hardcapping (in usecs). 0 to use system default",
    68  		},
    69  		cli.StringFlag{
    70  			Name:  "cpu-quota",
    71  			Usage: "CPU CFS hardcap limit (in usecs). Allowed cpu time in a given period",
    72  		},
    73  		cli.StringFlag{
    74  			Name:  "cpu-share",
    75  			Usage: "CPU shares (relative weight vs. other containers)",
    76  		},
    77  		cli.StringFlag{
    78  			Name:  "cpu-rt-period",
    79  			Usage: "CPU realtime period to be used for hardcapping (in usecs). 0 to use system default",
    80  		},
    81  		cli.StringFlag{
    82  			Name:  "cpu-rt-runtime",
    83  			Usage: "CPU realtime hardcap limit (in usecs). Allowed cpu time in a given period",
    84  		},
    85  		cli.StringFlag{
    86  			Name:  "cpuset-cpus",
    87  			Usage: "CPU(s) to use",
    88  		},
    89  		cli.StringFlag{
    90  			Name:  "cpuset-mems",
    91  			Usage: "Memory node(s) to use",
    92  		},
    93  		cli.StringFlag{
    94  			Name:   "kernel-memory",
    95  			Usage:  "(obsoleted; do not use)",
    96  			Hidden: true,
    97  		},
    98  		cli.StringFlag{
    99  			Name:   "kernel-memory-tcp",
   100  			Usage:  "(obsoleted; do not use)",
   101  			Hidden: true,
   102  		},
   103  		cli.StringFlag{
   104  			Name:  "memory",
   105  			Usage: "Memory limit (in bytes)",
   106  		},
   107  		cli.StringFlag{
   108  			Name:  "memory-reservation",
   109  			Usage: "Memory reservation or soft_limit (in bytes)",
   110  		},
   111  		cli.StringFlag{
   112  			Name:  "memory-swap",
   113  			Usage: "Total memory usage (memory + swap); set '-1' to enable unlimited swap",
   114  		},
   115  		cli.IntFlag{
   116  			Name:  "pids-limit",
   117  			Usage: "Maximum number of pids allowed in the container",
   118  		},
   119  		cli.StringFlag{
   120  			Name:  "l3-cache-schema",
   121  			Usage: "The string of Intel RDT/CAT L3 cache schema",
   122  		},
   123  		cli.StringFlag{
   124  			Name:  "mem-bw-schema",
   125  			Usage: "The string of Intel RDT/MBA memory bandwidth schema",
   126  		},
   127  	},
   128  	Action: func(context *cli.Context) error {
   129  		if err := checkArgs(context, 1, exactArgs); err != nil {
   130  			return err
   131  		}
   132  		container, err := getContainer(context)
   133  		if err != nil {
   134  			return err
   135  		}
   136  
   137  		r := specs.LinuxResources{
   138  			Memory: &specs.LinuxMemory{
   139  				Limit:       i64Ptr(0),
   140  				Reservation: i64Ptr(0),
   141  				Swap:        i64Ptr(0),
   142  				Kernel:      i64Ptr(0),
   143  				KernelTCP:   i64Ptr(0),
   144  			},
   145  			CPU: &specs.LinuxCPU{
   146  				Shares:          u64Ptr(0),
   147  				Quota:           i64Ptr(0),
   148  				Period:          u64Ptr(0),
   149  				RealtimeRuntime: i64Ptr(0),
   150  				RealtimePeriod:  u64Ptr(0),
   151  				Cpus:            "",
   152  				Mems:            "",
   153  			},
   154  			BlockIO: &specs.LinuxBlockIO{
   155  				Weight: u16Ptr(0),
   156  			},
   157  			Pids: &specs.LinuxPids{
   158  				Limit: 0,
   159  			},
   160  		}
   161  
   162  		config := container.Config()
   163  
   164  		if in := context.String("resources"); in != "" {
   165  			var (
   166  				f   *os.File
   167  				err error
   168  			)
   169  			switch in {
   170  			case "-":
   171  				f = os.Stdin
   172  			default:
   173  				f, err = os.Open(in)
   174  				if err != nil {
   175  					return err
   176  				}
   177  				defer f.Close()
   178  			}
   179  			err = json.NewDecoder(f).Decode(&r)
   180  			if err != nil {
   181  				return err
   182  			}
   183  		} else {
   184  			if val := context.Int("blkio-weight"); val != 0 {
   185  				r.BlockIO.Weight = u16Ptr(uint16(val))
   186  			}
   187  			if val := context.String("cpuset-cpus"); val != "" {
   188  				r.CPU.Cpus = val
   189  			}
   190  			if val := context.String("cpuset-mems"); val != "" {
   191  				r.CPU.Mems = val
   192  			}
   193  
   194  			for _, pair := range []struct {
   195  				opt  string
   196  				dest *uint64
   197  			}{
   198  				{"cpu-period", r.CPU.Period},
   199  				{"cpu-rt-period", r.CPU.RealtimePeriod},
   200  				{"cpu-share", r.CPU.Shares},
   201  			} {
   202  				if val := context.String(pair.opt); val != "" {
   203  					var err error
   204  					*pair.dest, err = strconv.ParseUint(val, 10, 64)
   205  					if err != nil {
   206  						return fmt.Errorf("invalid value for %s: %w", pair.opt, err)
   207  					}
   208  				}
   209  			}
   210  			for _, pair := range []struct {
   211  				opt  string
   212  				dest *int64
   213  			}{
   214  				{"cpu-quota", r.CPU.Quota},
   215  				{"cpu-rt-runtime", r.CPU.RealtimeRuntime},
   216  			} {
   217  				if val := context.String(pair.opt); val != "" {
   218  					var err error
   219  					*pair.dest, err = strconv.ParseInt(val, 10, 64)
   220  					if err != nil {
   221  						return fmt.Errorf("invalid value for %s: %w", pair.opt, err)
   222  					}
   223  				}
   224  			}
   225  			for _, pair := range []struct {
   226  				opt  string
   227  				dest *int64
   228  			}{
   229  				{"memory", r.Memory.Limit},
   230  				{"memory-swap", r.Memory.Swap},
   231  				{"kernel-memory", r.Memory.Kernel},
   232  				{"kernel-memory-tcp", r.Memory.KernelTCP},
   233  				{"memory-reservation", r.Memory.Reservation},
   234  			} {
   235  				if val := context.String(pair.opt); val != "" {
   236  					var v int64
   237  
   238  					if val != "-1" {
   239  						v, err = units.RAMInBytes(val)
   240  						if err != nil {
   241  							return fmt.Errorf("invalid value for %s: %w", pair.opt, err)
   242  						}
   243  					} else {
   244  						v = -1
   245  					}
   246  					*pair.dest = v
   247  				}
   248  			}
   249  
   250  			r.Pids.Limit = int64(context.Int("pids-limit"))
   251  		}
   252  
   253  		if *r.Memory.Kernel != 0 || *r.Memory.KernelTCP != 0 {
   254  			logrus.Warn("Kernel memory settings are ignored and will be removed")
   255  		}
   256  
   257  		// Update the values
   258  		config.Cgroups.Resources.BlkioWeight = *r.BlockIO.Weight
   259  
   260  		// Setting CPU quota and period independently does not make much sense,
   261  		// but historically runc allowed it and this needs to be supported
   262  		// to not break compatibility.
   263  		//
   264  		// For systemd cgroup drivers to set CPU quota/period correctly,
   265  		// it needs to know both values. For fs2 cgroup driver to be compatible
   266  		// with the fs driver, it also needs to know both values.
   267  		//
   268  		// Here in update, previously set values are available from config.
   269  		// If only one of {quota,period} is set and the other is not, leave
   270  		// the unset parameter at the old value (don't overwrite config).
   271  		p, q := *r.CPU.Period, *r.CPU.Quota
   272  		if (p == 0 && q == 0) || (p != 0 && q != 0) {
   273  			// both values are either set or unset (0)
   274  			config.Cgroups.Resources.CpuPeriod = p
   275  			config.Cgroups.Resources.CpuQuota = q
   276  		} else {
   277  			// one is set and the other is not
   278  			if p != 0 {
   279  				// set new period, leave quota at old value
   280  				config.Cgroups.Resources.CpuPeriod = p
   281  			} else if q != 0 {
   282  				// set new quota, leave period at old value
   283  				config.Cgroups.Resources.CpuQuota = q
   284  			}
   285  		}
   286  
   287  		config.Cgroups.Resources.CpuShares = *r.CPU.Shares
   288  		// CpuWeight is used for cgroupv2 and should be converted
   289  		config.Cgroups.Resources.CpuWeight = cgroups.ConvertCPUSharesToCgroupV2Value(*r.CPU.Shares)
   290  		config.Cgroups.Resources.CpuRtPeriod = *r.CPU.RealtimePeriod
   291  		config.Cgroups.Resources.CpuRtRuntime = *r.CPU.RealtimeRuntime
   292  		config.Cgroups.Resources.CpusetCpus = r.CPU.Cpus
   293  		config.Cgroups.Resources.CpusetMems = r.CPU.Mems
   294  		config.Cgroups.Resources.Memory = *r.Memory.Limit
   295  		config.Cgroups.Resources.MemoryReservation = *r.Memory.Reservation
   296  		config.Cgroups.Resources.MemorySwap = *r.Memory.Swap
   297  		config.Cgroups.Resources.PidsLimit = r.Pids.Limit
   298  		config.Cgroups.Resources.Unified = r.Unified
   299  
   300  		// Update Intel RDT
   301  		l3CacheSchema := context.String("l3-cache-schema")
   302  		memBwSchema := context.String("mem-bw-schema")
   303  		if l3CacheSchema != "" && !intelrdt.IsCATEnabled() {
   304  			return errors.New("Intel RDT/CAT: l3 cache schema is not enabled")
   305  		}
   306  
   307  		if memBwSchema != "" && !intelrdt.IsMBAEnabled() {
   308  			return errors.New("Intel RDT/MBA: memory bandwidth schema is not enabled")
   309  		}
   310  
   311  		if l3CacheSchema != "" || memBwSchema != "" {
   312  			// If intelRdt is not specified in original configuration, we just don't
   313  			// Apply() to create intelRdt group or attach tasks for this container.
   314  			// In update command, we could re-enable through IntelRdtManager.Apply()
   315  			// and then update intelrdt constraint.
   316  			if config.IntelRdt == nil {
   317  				state, err := container.State()
   318  				if err != nil {
   319  					return err
   320  				}
   321  				config.IntelRdt = &configs.IntelRdt{}
   322  				intelRdtManager := intelrdt.NewManager(&config, container.ID(), state.IntelRdtPath)
   323  				if err := intelRdtManager.Apply(state.InitProcessPid); err != nil {
   324  					return err
   325  				}
   326  			}
   327  			config.IntelRdt.L3CacheSchema = l3CacheSchema
   328  			config.IntelRdt.MemBwSchema = memBwSchema
   329  		}
   330  
   331  		// XXX(kolyshkin@): currently "runc update" is unable to change
   332  		// device configuration, so add this to skip device update.
   333  		// This helps in case an extra plugin (nvidia GPU) applies some
   334  		// configuration on top of what runc does.
   335  		// Note this field is not saved into container's state.json.
   336  		config.Cgroups.SkipDevices = true
   337  
   338  		return container.Set(config)
   339  	},
   340  }
   341  

View as plain text