Skip to content

Commit

Permalink
Move docker resource limit settings from server to agent (#3174)
Browse files Browse the repository at this point in the history
so you can set it per agent and not per server
  • Loading branch information
6543 authored Sep 26, 2024
1 parent b75a2ca commit 6ad20ce
Show file tree
Hide file tree
Showing 22 changed files with 221 additions and 293 deletions.
32 changes: 2 additions & 30 deletions cmd/server/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,36 +295,8 @@ var flags = append([]cli.Flag{
Usage: "How many retries of fetching the Woodpecker configuration from a forge are done before we fail",
Value: 3,
},
&cli.IntFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_MEM_SWAP"),
Name: "limit-mem-swap",
Usage: "maximum memory used for swap in bytes",
},
&cli.IntFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_MEM"),
Name: "limit-mem",
Usage: "maximum memory allowed in bytes",
},
&cli.IntFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_SHM_SIZE"),
Name: "limit-shm-size",
Usage: "docker compose /dev/shm allowed in bytes",
},
&cli.IntFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_CPU_QUOTA"),
Name: "limit-cpu-quota",
Usage: "impose a cpu quota",
},
&cli.IntFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_CPU_SHARES"),
Name: "limit-cpu-shares",
Usage: "change the cpu shares",
},
&cli.StringFlag{
Sources: cli.EnvVars("WOODPECKER_LIMIT_CPU_SET"),
Name: "limit-cpu-set",
Usage: "set the cpus allowed to execute containers",
},
//
// generic forge settings
//
&cli.StringFlag{
Name: "forge-url",
Expand Down
8 changes: 0 additions & 8 deletions cmd/server/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,6 @@ func setupEvilGlobals(ctx context.Context, c *cli.Command, s store.Store) error
server.Config.Pipeline.DefaultTimeout = c.Int("default-pipeline-timeout")
server.Config.Pipeline.MaxTimeout = c.Int("max-pipeline-timeout")

// limits
server.Config.Pipeline.Limits.MemSwapLimit = c.Int("limit-mem-swap")
server.Config.Pipeline.Limits.MemLimit = c.Int("limit-mem")
server.Config.Pipeline.Limits.ShmSize = c.Int("limit-shm-size")
server.Config.Pipeline.Limits.CPUQuota = c.Int("limit-cpu-quota")
server.Config.Pipeline.Limits.CPUShares = c.Int("limit-cpu-shares")
server.Config.Pipeline.Limits.CPUSet = c.String("limit-cpu-set")

// backend options for pipeline compiler
server.Config.Pipeline.Proxy.No = c.String("backend-no-proxy")
server.Config.Pipeline.Proxy.HTTP = c.String("backend-http-proxy")
Expand Down
38 changes: 0 additions & 38 deletions docs/docs/30-administration/10-server-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,44 +476,6 @@ Supported variables:

---

### `WOODPECKER_LIMIT_MEM_SWAP`

> Default: `0`
The maximum amount of memory a single pipeline container is allowed to swap to disk, configured in bytes. There is no limit if `0`.

### `WOODPECKER_LIMIT_MEM`

> Default: `0`
The maximum amount of memory a single pipeline container can use, configured in bytes. There is no limit if `0`.

### `WOODPECKER_LIMIT_SHM_SIZE`

> Default: `0`
The maximum amount of memory of `/dev/shm` allowed in bytes. There is no limit if `0`.

### `WOODPECKER_LIMIT_CPU_QUOTA`

> Default: `0`
The number of microseconds per CPU period that the container is limited to before throttled. There is no limit if `0`.

### `WOODPECKER_LIMIT_CPU_SHARES`

> Default: `0`
The relative weight vs. other containers.

### `WOODPECKER_LIMIT_CPU_SET`

> Default: empty
Comma-separated list to limit the specific CPUs or cores a pipeline container can use.

Example: `WOODPECKER_LIMIT_CPU_SET=1,2`

### `WOODPECKER_CONFIG_SERVICE_ENDPOINT`

> Default: empty
Expand Down
38 changes: 38 additions & 0 deletions docs/docs/30-administration/22-backends/10-docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,41 @@ Enable IPv6 for the networks used by pipeline containers (steps). Make sure you
List of default volumes separated by comma to be mounted to all pipeline containers (steps). For example to use custom CA
certificates installed on host and host timezone use `/etc/ssl/certs:/etc/ssl/certs:ro,/etc/timezone:/etc/timezone`.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_MEM_SWAP`

> Default: `0`
The maximum amount of memory a single pipeline container is allowed to swap to disk, configured in bytes. There is no limit if `0`.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_MEM`

> Default: `0`
The maximum amount of memory a single pipeline container can use, configured in bytes. There is no limit if `0`.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_SHM_SIZE`

> Default: `0`
The maximum amount of memory of `/dev/shm` allowed in bytes. There is no limit if `0`.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_CPU_QUOTA`

> Default: `0`
The number of microseconds per CPU period that the container is limited to before throttled. There is no limit if `0`.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_CPU_SHARES`

> Default: `0`
The relative weight vs. other containers.

### `WOODPECKER_BACKEND_DOCKER_LIMIT_CPU_SET`

> Default: empty
Comma-separated list to limit the specific CPUs or cores a pipeline container can use.

Example: `WOODPECKER_BACKEND_DOCKER_LIMIT_CPU_SET=1,2`
3 changes: 2 additions & 1 deletion docs/docs/91-migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ Some versions need some changes to the server configuration or the pipeline conf

## `next`

- Set `/woodpecker` as defautl workdir for the **woodpecker-cli** container
- Removed built-in environment variables:
- `CI_COMMIT_URL` use `CI_PIPELINE_FORGE_URL`
- `CI_STEP_FINISHED` as empty during execution
- `CI_PIPELINE_FINISHED` as empty during execution
- `CI_PIPELINE_STATUS` was always `success`
- `CI_STEP_STATUS` was always `success`
- Set `/woodpecker` as defautl workdir for the **woodpecker-cli** container
- Move docker resource limit settings from server into agent configuration
- Rename server environment variable `WOODPECKER_ESCALATE` to `WOODPECKER_PLUGINS_PRIVILEGED`
- All default privileged plugins (like `woodpeckerci/plugin-docker-buildx`) were removed. Please carefully [re-add those plugins](./30-administration/10-server-config.md#woodpecker_plugins_privileged) you trust and rely on.
- `WOODPECKER_DEFAULT_CLONE_IMAGE` got depricated use `WOODPECKER_DEFAULT_CLONE_PLUGIN`
Expand Down
71 changes: 71 additions & 0 deletions pipeline/backend/docker/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2024 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package docker

import (
"fmt"
"strings"

"github.com/rs/zerolog/log"
"github.com/urfave/cli/v3"
)

type config struct {
enableIPv6 bool
network string
volumes []string
resourceLimit resourceLimit
}

type resourceLimit struct {
MemSwapLimit int64
MemLimit int64
ShmSize int64
CPUQuota int64
CPUShares int64
CPUSet string
}

func configFromCli(c *cli.Command) (config, error) {
conf := config{
enableIPv6: c.Bool("backend-docker-ipv6"),
network: c.String("backend-docker-network"),
resourceLimit: resourceLimit{
MemSwapLimit: c.Int("backend-docker-limit-mem-swap"),
MemLimit: c.Int("backend-docker-limit-mem"),
ShmSize: c.Int("backend-docker-limit-shm-size"),
CPUQuota: c.Int("backend-docker-limit-cpu-quota"),
CPUShares: c.Int("backend-docker-limit-cpu-shares"),
CPUSet: c.String("backend-docker-limit-cpu-set"),
},
}

volumes := strings.Split(c.String("backend-docker-volumes"), ",")
conf.volumes = make([]string, 0, len(volumes))
// Validate provided volume definitions
for _, v := range volumes {
if v == "" {
continue
}
parts, err := splitVolumeParts(v)
if err != nil {
log.Error().Err(err).Msgf("can not parse volume config")
return conf, fmt.Errorf("invalid volume '%s' provided in WOODPECKER_BACKEND_DOCKER_VOLUMES: %w", v, err)
}
conf.volumes = append(conf.volumes, strings.Join(parts, ":"))
}

return conf, nil
}
14 changes: 7 additions & 7 deletions pipeline/backend/docker/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,20 @@ func toContainerName(step *types.Step) string {
}

// returns a container host configuration.
func toHostConfig(step *types.Step) *container.HostConfig {
func toHostConfig(step *types.Step, conf *config) *container.HostConfig {
config := &container.HostConfig{
Resources: container.Resources{
CPUQuota: step.CPUQuota,
CPUShares: step.CPUShares,
CpusetCpus: step.CPUSet,
Memory: step.MemLimit,
MemorySwap: step.MemSwapLimit,
CPUQuota: conf.resourceLimit.CPUQuota,
CPUShares: conf.resourceLimit.CPUShares,
CpusetCpus: conf.resourceLimit.CPUSet,
Memory: conf.resourceLimit.MemLimit,
MemorySwap: conf.resourceLimit.MemSwapLimit,
},
ShmSize: conf.resourceLimit.ShmSize,
LogConfig: container.LogConfig{
Type: "json-file",
},
Privileged: step.Privileged,
ShmSize: step.ShmSize,
}

if len(step.NetworkMode) != 0 {
Expand Down
65 changes: 36 additions & 29 deletions pipeline/backend/docker/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,37 +196,44 @@ func TestToConfigSmall(t *testing.T) {
}

func TestToConfigFull(t *testing.T) {
engine := docker{info: system.Info{OSType: "linux/riscv64"}}
engine := docker{
info: system.Info{OSType: "linux/riscv64"},
config: config{
enableIPv6: true,
resourceLimit: resourceLimit{
MemSwapLimit: 12,
MemLimit: 13,
ShmSize: 14,
CPUQuota: 15,
CPUShares: 16,
},
},
}

conf := engine.toConfig(&backend.Step{
Name: "test",
UUID: "09238932",
Type: backend.StepTypeCommands,
Image: "golang:1.2.3",
Pull: true,
Detached: true,
Privileged: true,
WorkingDir: "/src/abc",
Environment: map[string]string{"TAGS": "sqlite"},
Commands: []string{"go test", "go vet ./..."},
ExtraHosts: []backend.HostAlias{{Name: "t", IP: "1.2.3.4"}},
Volumes: []string{"/cache:/cache"},
Tmpfs: []string{"/tmp"},
Devices: []string{"/dev/sdc"},
Networks: []backend.Conn{{Name: "extra-net", Aliases: []string{"extra.net"}}},
DNS: []string{"9.9.9.9", "8.8.8.8"},
DNSSearch: nil,
MemSwapLimit: 12,
MemLimit: 13,
ShmSize: 14,
CPUQuota: 15,
CPUShares: 16,
OnFailure: true,
OnSuccess: true,
Failure: "fail",
AuthConfig: backend.Auth{Username: "user", Password: "123456"},
NetworkMode: "bridge",
Ports: []backend.Port{{Number: 21}, {Number: 22}},
Name: "test",
UUID: "09238932",
Type: backend.StepTypeCommands,
Image: "golang:1.2.3",
Pull: true,
Detached: true,
Privileged: true,
WorkingDir: "/src/abc",
Environment: map[string]string{"TAGS": "sqlite"},
Commands: []string{"go test", "go vet ./..."},
ExtraHosts: []backend.HostAlias{{Name: "t", IP: "1.2.3.4"}},
Volumes: []string{"/cache:/cache"},
Tmpfs: []string{"/tmp"},
Devices: []string{"/dev/sdc"},
Networks: []backend.Conn{{Name: "extra-net", Aliases: []string{"extra.net"}}},
DNS: []string{"9.9.9.9", "8.8.8.8"},
DNSSearch: nil,
OnFailure: true,
OnSuccess: true,
Failure: "fail",
AuthConfig: backend.Auth{Username: "user", Password: "123456"},
NetworkMode: "bridge",
Ports: []backend.Port{{Number: 21}, {Number: 22}},
})

assert.NotNil(t, conf)
Expand Down
Loading

0 comments on commit 6ad20ce

Please sign in to comment.