docker stats

This commit is contained in:
Robin Appelman 2021-03-29 21:58:15 +02:00
commit 0a571c9039
5 changed files with 484 additions and 3 deletions

61
src/docker.rs Normal file
View file

@ -0,0 +1,61 @@
use bollard::container::Stats;
use bollard::Docker;
use color_eyre::Result;
use futures_util::stream::{iter, Stream, StreamExt};
use std::fmt::Write;
pub struct Container {
name: String,
memory: u64,
cpu_time: u64,
}
impl From<Stats> for Container {
fn from(stats: Stats) -> Self {
Container {
name: stats.name,
memory: stats.memory_stats.usage.unwrap_or_default(),
cpu_time: stats.cpu_stats.cpu_usage.total_usage,
}
}
}
impl Container {
pub fn write<W: Write>(&self, mut w: W, hostname: &str) {
writeln!(
&mut w,
"container_memory{{host=\"{}\", container=\"{}\"}} {}",
hostname, self.name, self.memory
)
.ok();
writeln!(
&mut w,
"container_cpu_time{{host=\"{}\", container=\"{}\"}} {}",
hostname, self.name, self.cpu_time
)
.ok();
}
}
pub async fn get_docker() -> Option<Docker> {
match Docker::connect_with_local_defaults() {
Ok(docker) => docker
.list_containers::<String>(None)
.await
.ok()
.map(|_| docker),
Err(_) => None,
}
}
pub async fn stat(docker: Docker) -> Result<impl Stream<Item = Container>> {
let containers = docker.list_containers::<String>(None).await?;
Ok(iter(containers.into_iter()).filter_map(move |container| {
let docker = docker.clone();
async move {
let id = container.id.unwrap();
let stats: Stats = docker.stats(&id, None).next().await?.ok()?;
Some(stats.into())
}
}))
}

View file

@ -1,3 +1,4 @@
pub mod docker;
pub mod sensors;
pub mod zfs;

View file

@ -1,4 +1,8 @@
use bollard::Docker;
use color_eyre::{Report, Result};
use futures_util::pin_mut;
use futures_util::StreamExt;
use palantir::docker::{get_docker, stat, Container};
use palantir::get_metrics;
use warp::reject::Reject;
use warp::{Filter, Rejection};
@ -15,8 +19,23 @@ impl From<Report> for ReportRejection {
impl Reject for ReportRejection {}
async fn serve_metrics() -> Result<String, Rejection> {
get_metrics()
async fn serve_inner(docker: Option<Docker>) -> Result<String> {
let mut metrics = get_metrics()?;
if let Some(docker) = docker {
let hostname = palantir::sensors::hostname()?;
let containers = stat(docker).await?;
pin_mut!(containers);
while let Some(container) = containers.next().await {
let container: Container = container;
container.write(&mut metrics, &hostname);
}
}
Ok(metrics)
}
async fn serve_metrics(docker: Option<Docker>) -> Result<String, Rejection> {
serve_inner(docker)
.await
.map_err(ReportRejection::from)
.map_err(warp::reject::custom)
}
@ -34,7 +53,10 @@ async fn main() -> Result<()> {
})
.expect("Error setting Ctrl-C handler");
let metrics = warp::path!("metrics").and_then(serve_metrics);
let docker = get_docker().await;
let docker = warp::any().map(move || docker.clone());
let metrics = warp::path!("metrics").and(docker).and_then(serve_metrics);
warp::serve(metrics).run(([0, 0, 0, 0], host_port)).await;
Ok(())