Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move docker resource limit settings from server to agent #3174

Merged
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

Check warning on line 60 in pipeline/backend/docker/config.go

View check run for this annotation

Codecov / codecov/patch

pipeline/backend/docker/config.go#L41-L60

Added lines #L41 - L60 were not covered by tests
}
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, ":"))

Check warning on line 67 in pipeline/backend/docker/config.go

View check run for this annotation

Codecov / codecov/patch

pipeline/backend/docker/config.go#L62-L67

Added lines #L62 - L67 were not covered by tests
}

return conf, nil
}

Check warning on line 71 in pipeline/backend/docker/config.go

View check run for this annotation

Codecov / codecov/patch

pipeline/backend/docker/config.go#L70-L71

Added lines #L70 - L71 were not covered by tests
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 @@
}

// 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,

Check warning on line 78 in pipeline/backend/docker/convert.go

View check run for this annotation

Codecov / codecov/patch

pipeline/backend/docker/convert.go#L74-L78

Added lines #L74 - L78 were not covered by tests
},
ShmSize: conf.resourceLimit.ShmSize,

Check warning on line 80 in pipeline/backend/docker/convert.go

View check run for this annotation

Codecov / codecov/patch

pipeline/backend/docker/convert.go#L80

Added line #L80 was not covered by tests
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