1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.dockerapi;
import java.util.Objects;
/**
* @author valerijf
*/
public class ContainerResources {
public static final ContainerResources UNLIMITED = ContainerResources.from(0, 0, 0);
public static final int CPU_PERIOD_US = 100_000; // 100 ms
/**
* Hard limit on container's CPU usage: Implemented using Completely Fair Scheduler (CFS) by allocating a given
* time within a given period, Container's processes are not bound to any specific CPU, which may create significant
* performance degradation as processes are scheduled on another CPU after exhausting the quota.
*/
private final double cpus;
/**
* Soft limit on container's CPU usage: When plenty of CPU cycles are available, all containers use as much
* CPU as they need. It prioritizes container CPU resources for the available CPU cycles.
* It does not guarantee or reserve any specific CPU access.
*/
private final int cpuShares;
/** The maximum amount, in bytes, of memory the container can use. */
private final long memoryBytes;
ContainerResources(double cpus, int cpuShares, long memoryBytes) {
this.cpus = cpus;
this.cpuShares = cpuShares;
this.memoryBytes = memoryBytes;
if (cpus < 0)
throw new IllegalArgumentException("CPUs must be a positive number or 0 for unlimited, was " + cpus);
if (cpuShares != 0 && (cpuShares < 2 || cpuShares > 262_144))
throw new IllegalArgumentException("CPU shares must be a positive integer in [2, 262144] or 0 for unlimited, was " + cpuShares);
if (memoryBytes < 0)
throw new IllegalArgumentException("memoryBytes must be a positive integer or 0 for unlimited, was " + memoryBytes);
}
/**
* Create container resources from required fields.
*
* @param maxVcpu the amount of vcpu that allocation policies should allocate exclusively to this container.
* This is a hard upper limit. To allow an unlimited amount use 0.
* @param minVcpu the minimal amount of vcpu dedicated to this container.
* To avoid dedicating any cpu at all, use 0.
* @param memoryGb the amount of memory that allocation policies should allocate to this container.
* This is a hard upper limit. To allow the container to allocate an unlimited amount use 0.
* @return the container resources encapsulating the parameters
*/
public static ContainerResources from(double maxVcpu, double minVcpu, double memoryGb) {
return new ContainerResources(maxVcpu,
(int) Math.round(32 * minVcpu),
(long) ((1L << 30) * memoryGb));
}
public double cpus() {
return cpus;
}
// Although docker allows to update cpu quota to 0, this is not a legal value, must be set -1 for unlimited
// See: https://github.com/docker/for-linux/issues/558
public int cpuQuota() {
return cpus > 0 ? (int) (cpus * CPU_PERIOD_US) : -1;
}
/** Duration (in µs) of a single period used as the basis for process scheduling */
public int cpuPeriod() {
return CPU_PERIOD_US;
}
public int cpuShares() {
return cpuShares;
}
public long memoryBytes() {
return memoryBytes;
}
/** Returns true iff the memory component(s) of between <code>this</code> and <code>other</code> are equal */
public boolean equalsMemory(ContainerResources other) {
return memoryBytes == other.memoryBytes;
}
/** Returns true iff the CPU component(s) of between <code>this</code> and <code>other</code> are equal */
public boolean equalsCpu(ContainerResources other) {
return Math.abs(other.cpus - cpus) < 0.0001 && cpuShares == other.cpuShares;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContainerResources that = (ContainerResources) o;
return equalsMemory(that) && equalsCpu(that);
}
@Override
public int hashCode() {
return Objects.hash(cpus, cpuShares, memoryBytes);
}
/** Returns only the memory component(s) of {@link #toString()} */
public String toStringMemory() {
return (memoryBytes > 0 ? memoryBytes + "B" : "unlimited") + " memory";
}
/** Returns only the CPU component(s) of {@link #toString()} */
public String toStringCpu() {
return (cpus > 0 ? String.format("%.2f", cpus) : "unlimited") +" CPUs, " +
(cpuShares > 0 ? cpuShares : "unlimited") + " CPU Shares";
}
@Override
public String toString() {
return toStringCpu() + ", " + toStringMemory();
}
public ContainerResources withMemoryBytes(long memoryBytes) {
return new ContainerResources(cpus, cpuShares, memoryBytes);
}
}
|