summaryrefslogtreecommitdiffstats
path: root/docker-api
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-02-01 19:32:58 +0100
committerMartin Polden <mpolden@mpolden.no>2021-02-02 11:11:37 +0100
commit34d699e6f5dabd5618f137fd9e76d739605c832a (patch)
treef82b809e567fe9d1c7ac06c74b59bc0d75574d5b /docker-api
parentf9f830402992f3c56334879c891820181e6a43fe (diff)
Decouple ContainerStats from docker-java
Diffstat (limited to 'docker-api')
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java193
-rw-r--r--docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java42
2 files changed, 180 insertions, 55 deletions
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
index 797dffdef1f..517c1f4d138 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/ContainerStats.java
@@ -1,40 +1,25 @@
// Copyright 2017 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 com.fasterxml.jackson.databind.ObjectMapper;
-import com.github.dockerjava.api.model.CpuStatsConfig;
-import com.github.dockerjava.api.model.MemoryStatsConfig;
-import com.github.dockerjava.api.model.StatisticNetworksConfig;
-import com.github.dockerjava.api.model.Statistics;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
import java.util.Map;
-import java.util.Optional;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
+import java.util.Objects;
/**
- * Wrapper class for {@link com.github.dockerjava.api.model.Statistics} to prevent leaking from docker-java library.
+ * CPU, memory and network statistics collected from a container.
*
* @author freva
*/
+// TODO: Move this to node-admin when docker-api module can be removed
public class ContainerStats {
+
private final Map<String, NetworkStats> networkStatsByInterface;
private final MemoryStats memoryStats;
private final CpuStats cpuStats;
- ContainerStats(Statistics statistics) {
- // Network stats are null when container uses host network
- this.networkStatsByInterface = Optional.ofNullable(statistics.getNetworks()).orElseGet(Map::of)
- .entrySet().stream()
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- e -> new NetworkStats(e.getValue()),
- (u, v) -> { throw new IllegalStateException(); },
- TreeMap::new));
- this.memoryStats = new MemoryStats(statistics.getMemoryStats());
- this.cpuStats = new CpuStats(statistics.getCpuStats());
+ public ContainerStats(Map<String, NetworkStats> networkStatsByInterface, MemoryStats memoryStats, CpuStats cpuStats) {
+ this.networkStatsByInterface = Map.copyOf(Objects.requireNonNull(networkStatsByInterface));
+ this.memoryStats = Objects.requireNonNull(memoryStats);
+ this.cpuStats = Objects.requireNonNull(cpuStats);
}
public Map<String, NetworkStats> getNetworks() {
@@ -49,7 +34,22 @@ public class ContainerStats {
return cpuStats;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ContainerStats that = (ContainerStats) o;
+ return networkStatsByInterface.equals(that.networkStatsByInterface) && memoryStats.equals(that.memoryStats) && cpuStats.equals(that.cpuStats);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(networkStatsByInterface, memoryStats, cpuStats);
+ }
+
+ /** Statistics for network usage */
public static class NetworkStats {
+
private final long rxBytes;
private final long rxDropped;
private final long rxErrors;
@@ -57,40 +57,109 @@ public class ContainerStats {
private final long txDropped;
private final long txErrors;
- private NetworkStats(StatisticNetworksConfig statisticNetworksConfig) {
- this.rxBytes = statisticNetworksConfig.getRxBytes();
- this.rxDropped = statisticNetworksConfig.getRxDropped();
- this.rxErrors = statisticNetworksConfig.getRxErrors();
- this.txBytes = statisticNetworksConfig.getTxBytes();
- this.txDropped = statisticNetworksConfig.getTxDropped();
- this.txErrors = statisticNetworksConfig.getTxErrors();
+ public NetworkStats(long rxBytes, long rxDropped, long rxErrors, long txBytes, long txDropped, long txErrors) {
+ this.rxBytes = rxBytes;
+ this.rxDropped = rxDropped;
+ this.rxErrors = rxErrors;
+ this.txBytes = txBytes;
+ this.txDropped = txDropped;
+ this.txErrors = txErrors;
}
+ /** Returns received bytes */
public long getRxBytes() { return this.rxBytes; }
+
+ /** Returns received bytes, which was dropped */
public long getRxDropped() { return this.rxDropped; }
+
+ /** Returns received errors */
public long getRxErrors() { return this.rxErrors; }
+
+ /** Returns transmitted bytes */
public long getTxBytes() { return this.txBytes; }
+
+ /** Returns transmitted bytes, which was dropped */
public long getTxDropped() { return this.txDropped; }
+
+ /** Returns transmission errors */
public long getTxErrors() { return this.txErrors; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NetworkStats that = (NetworkStats) o;
+ return rxBytes == that.rxBytes && rxDropped == that.rxDropped && rxErrors == that.rxErrors && txBytes == that.txBytes && txDropped == that.txDropped && txErrors == that.txErrors;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rxBytes, rxDropped, rxErrors, txBytes, txDropped, txErrors);
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkStats{" +
+ "rxBytes=" + rxBytes +
+ ", rxDropped=" + rxDropped +
+ ", rxErrors=" + rxErrors +
+ ", txBytes=" + txBytes +
+ ", txDropped=" + txDropped +
+ ", txErrors=" + txErrors +
+ '}';
+ }
+
}
- public class MemoryStats {
+ /** Statistics for memory usage */
+ public static class MemoryStats {
+
private final long cache;
private final long usage;
private final long limit;
- private MemoryStats(MemoryStatsConfig memoryStats) {
- this.cache = memoryStats.getStats().getCache();
- this.usage = memoryStats.getUsage();
- this.limit = memoryStats.getLimit();
+ public MemoryStats(long cache, long usage, long limit) {
+ this.cache = cache;
+ this.usage = usage;
+ this.limit = limit;
}
+ /** Returns memory used by cache in bytes */
public long getCache() { return this.cache; }
+
+ /** Returns memory usage in bytes */
public long getUsage() { return this.usage; }
+
+ /** Returns memory limit in bytes */
public long getLimit() { return this.limit; }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ MemoryStats that = (MemoryStats) o;
+ return cache == that.cache && usage == that.usage && limit == that.limit;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cache, usage, limit);
+ }
+
+ @Override
+ public String toString() {
+ return "MemoryStats{" +
+ "cache=" + cache +
+ ", usage=" + usage +
+ ", limit=" + limit +
+ '}';
+ }
+
}
- public class CpuStats {
+ /** Statistics for CPU usage */
+ public static class CpuStats {
+
private final int onlineCpus;
private final long systemCpuUsage;
private final long totalUsage;
@@ -99,15 +168,15 @@ public class ContainerStats {
private final long throttlingActivePeriods;
private final long throttledPeriods;
- public CpuStats(CpuStatsConfig cpuStats) {
- // Added in 1.27
- this.onlineCpus = cpuStats.getCpuUsage().getPercpuUsage().size();
- this.systemCpuUsage = cpuStats.getSystemCpuUsage();
- this.totalUsage = cpuStats.getCpuUsage().getTotalUsage();
- this.usageInKernelMode = cpuStats.getCpuUsage().getUsageInKernelmode();
- this.throttledTime = cpuStats.getThrottlingData().getThrottledTime();
- this.throttlingActivePeriods = cpuStats.getThrottlingData().getPeriods();
- this.throttledPeriods = cpuStats.getThrottlingData().getThrottledPeriods();
+ public CpuStats(int onlineCpus, long systemCpuUsage, long totalUsage, long usageInKernelMode,
+ long throttledTime, long throttlingActivePeriods, long throttledPeriods) {
+ this.onlineCpus = onlineCpus;
+ this.systemCpuUsage = systemCpuUsage;
+ this.totalUsage = totalUsage;
+ this.usageInKernelMode = usageInKernelMode;
+ this.throttledTime = throttledTime;
+ this.throttlingActivePeriods = throttlingActivePeriods;
+ this.throttledPeriods = throttledPeriods;
}
public int getOnlineCpus() { return this.onlineCpus; }
@@ -129,15 +198,33 @@ public class ContainerStats {
/** Number of periods this container hit the throttling limit */
public long getThrottledPeriods() { return throttledPeriods; }
- }
- // For testing only, create ContainerStats from JSON returned by docker daemon stats API
- public static ContainerStats fromJson(String json) {
- try {
- Statistics statistics = new ObjectMapper().readValue(json, Statistics.class);
- return new ContainerStats(statistics);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CpuStats cpuStats = (CpuStats) o;
+ return onlineCpus == cpuStats.onlineCpus && systemCpuUsage == cpuStats.systemCpuUsage && totalUsage == cpuStats.totalUsage && usageInKernelMode == cpuStats.usageInKernelMode && throttledTime == cpuStats.throttledTime && throttlingActivePeriods == cpuStats.throttlingActivePeriods && throttledPeriods == cpuStats.throttledPeriods;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(onlineCpus, systemCpuUsage, totalUsage, usageInKernelMode, throttledTime, throttlingActivePeriods, throttledPeriods);
}
+
+ @Override
+ public String toString() {
+ return "CpuStats{" +
+ "onlineCpus=" + onlineCpus +
+ ", systemCpuUsage=" + systemCpuUsage +
+ ", totalUsage=" + totalUsage +
+ ", usageInKernelMode=" + usageInKernelMode +
+ ", throttledTime=" + throttledTime +
+ ", throttlingActivePeriods=" + throttlingActivePeriods +
+ ", throttledPeriods=" + throttledPeriods +
+ '}';
+ }
+
}
+
}
diff --git a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
index a45855764ed..630efb7990f 100644
--- a/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
+++ b/docker-api/src/main/java/com/yahoo/vespa/hosted/dockerapi/DockerEngine.java
@@ -1,6 +1,7 @@
// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.dockerapi;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
@@ -33,6 +34,8 @@ import com.yahoo.vespa.hosted.dockerapi.metrics.Gauge;
import com.yahoo.vespa.hosted.dockerapi.metrics.Metrics;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
@@ -43,6 +46,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
+import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -198,8 +202,7 @@ public class DockerEngine implements ContainerEngine {
try {
DockerStatsCallback statsCallback = dockerClient.statsCmd(containerName.asString()).exec(new DockerStatsCallback());
statsCallback.awaitCompletion(5, TimeUnit.SECONDS);
-
- return statsCallback.stats.map(ContainerStats::new);
+ return statsCallback.stats.map(DockerEngine::containerStatsFrom);
} catch (NotFoundException ignored) {
return Optional.empty();
} catch (RuntimeException | InterruptedException e) {
@@ -437,4 +440,39 @@ public class DockerEngine implements ContainerEngine {
return DockerClientImpl.getInstance(dockerClientConfig)
.withDockerCmdExecFactory(dockerFactory);
}
+
+ private static ContainerStats containerStatsFrom(Statistics statistics) {
+ return new ContainerStats(Optional.ofNullable(statistics.getNetworks()).orElseGet(Map::of)
+ .entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ e -> new ContainerStats.NetworkStats(e.getValue().getRxBytes(), e.getValue().getRxDropped(),
+ e.getValue().getRxErrors(), e.getValue().getTxBytes(),
+ e.getValue().getTxDropped(), e.getValue().getTxErrors()),
+ (u, v) -> {
+ throw new IllegalStateException();
+ },
+ TreeMap::new)),
+ new ContainerStats.MemoryStats(statistics.getMemoryStats().getStats().getCache(),
+ statistics.getMemoryStats().getUsage(),
+ statistics.getMemoryStats().getLimit()),
+ new ContainerStats.CpuStats(statistics.getCpuStats().getCpuUsage().getPercpuUsage().size(),
+ statistics.getCpuStats().getSystemCpuUsage(),
+ statistics.getCpuStats().getCpuUsage().getTotalUsage(),
+ statistics.getCpuStats().getCpuUsage().getUsageInKernelmode(),
+ statistics.getCpuStats().getThrottlingData().getThrottledTime(),
+ statistics.getCpuStats().getThrottlingData().getPeriods(),
+ statistics.getCpuStats().getThrottlingData().getThrottledPeriods()));
+ }
+
+ // For testing only, create ContainerStats from JSON returned by docker daemon stats API
+ public static ContainerStats statsFromJson(String json) {
+ try {
+ Statistics statistics = new ObjectMapper().readValue(json, Statistics.class);
+ return containerStatsFrom(statistics);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
}