sparse-intern-71089
04/24/2019, 5:14 PMfull-dress-10026
04/24/2019, 5:16 PM.metrics
property.white-balloon-205
lemon-spoon-91807
04/24/2019, 5:23 PMlemon-spoon-91807
04/24/2019, 5:23 PMfull-dress-10026
04/24/2019, 5:23 PMlemon-spoon-91807
04/24/2019, 5:24 PMlemon-spoon-91807
04/24/2019, 5:24 PMlemon-spoon-91807
04/24/2019, 5:24 PMlemon-spoon-91807
04/24/2019, 5:24 PMnew awsx.cloudwatch.Metric({ args })
lemon-spoon-91807
04/24/2019, 5:25 PMfull-dress-10026
04/24/2019, 5:25 PMlemon-spoon-91807
04/24/2019, 5:29 PMlemon-spoon-91807
04/24/2019, 5:29 PMlemon-spoon-91807
04/24/2019, 5:30 PMfull-dress-10026
04/24/2019, 5:30 PMlemon-spoon-91807
04/24/2019, 5:30 PMawsx.ecs.metrics.memoryUtilization({ service })
lemon-spoon-91807
04/24/2019, 5:31 PMlemon-spoon-91807
04/24/2019, 5:31 PMfull-dress-10026
04/24/2019, 5:32 PMlemon-spoon-91807
04/24/2019, 5:32 PMlemon-spoon-91807
04/24/2019, 5:32 PMlemon-spoon-91807
04/24/2019, 5:33 PMfull-dress-10026
04/24/2019, 5:38 PMnew awsx.cloudwatch.LineGraphMetricWidget({
title: "Metrics Streams CPU",
metrics: [
awsx.ecs.metrics.cpuUtilization({
cluster: dashboardArgs.metricsStreamsService.cluster,
service: dashboardArgs.metricsStreamsService.service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.cpuUtilization({
cluster: dashboardArgs.metricsStreamsService.cluster,
service: dashboardArgs.metricsStreamsService.service,
statistic: "Maximum",
label: "max"
})
]
})
One of those would be created per service, resulting in a lot of copy/paste. Not sure if this is something that could be handled by this API.lemon-spoon-91807
04/24/2019, 5:39 PMlemon-spoon-91807
04/24/2019, 5:39 PMfor
-loop over your serviceslemon-spoon-91807
04/24/2019, 5:39 PMlemon-spoon-91807
04/24/2019, 5:39 PMawsx.ecs.metrics.cpuUtilization({
cluster: dashboardArgs.metricsStreamsService.cluster,
service: dashboardArgs.metricsStreamsService.service,
lemon-spoon-91807
04/24/2019, 5:39 PMlemon-spoon-91807
04/24/2019, 5:40 PMawsx.ecs.metrics.cpuUtilization({
service: dashboardArgs.metricsStreamsService,
full-dress-10026
04/24/2019, 5:40 PMlemon-spoon-91807
04/24/2019, 5:40 PMlemon-spoon-91807
04/24/2019, 5:40 PMlemon-spoon-91807
04/24/2019, 5:50 PMlemon-spoon-91807
04/24/2019, 5:50 PMfull-dress-10026
04/24/2019, 5:50 PMlemon-spoon-91807
04/24/2019, 5:50 PMfull-dress-10026
04/24/2019, 6:15 PMwidgets
? For example:
function fargateServiceCpuMemGraphs(service: awsx.ecs.FargateService) {
return service.service.name.apply(serviceName => {
return [
new awsx.cloudwatch.LineGraphMetricWidget({
title: `${serviceName} CPU`,
metrics: [
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
}),
new awsx.cloudwatch.LineGraphMetricWidget({
title: `${serviceName} Memory`,
metrics: [
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
})
];
});
}
new awsx.cloudwatch.Dashboard("cw-dashboard-" + env, {
name: `vm-scaler-${env}`,
widgets: pulumi.all([
fargateServiceCpuMemGraphs(dashboardArgs.workloadMetricsWorkerService),
fargateServiceCpuMemGraphs(dashboardArgs.workloadMetricsWorkerService)
]).apply(function ([graphs1, graphs2]) {
return graphs1.concat(graphs2);
})
});
lemon-spoon-91807
04/24/2019, 6:23 PMfull-dress-10026
04/24/2019, 6:23 PMfunction fargateServiceCpuMemGraphs(service: awsx.ecs.FargateService) {
const serviceName = service.service.name;
return [
new awsx.cloudwatch.LineGraphMetricWidget({
title: serviceName.apply(n => `${n} CPU`),
metrics: [
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
}),
new awsx.cloudwatch.LineGraphMetricWidget({
title: serviceName.apply(n => `${n} Memory`),
metrics: [
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
})
];
}
lemon-spoon-91807
04/24/2019, 6:23 PMtitle: pulumi.interpolate `${service.name} CPU`,
full-dress-10026
04/24/2019, 6:23 PMlemon-spoon-91807
04/24/2019, 6:24 PMlemon-spoon-91807
04/24/2019, 6:24 PMservice: service,
is also redundant in JS. but it's a personal pref. you an say: service
full-dress-10026
04/24/2019, 6:25 PM${serviceName} CPU
),` gives Argument of type 'string' is not assignable to parameter of type 'TemplateStringsArray'
.
Coming from Clojure, I prefer the explicit syntax 🙂lemon-spoon-91807
04/24/2019, 6:25 PMpulumi.interpolate`${service.name} CPU`
lemon-spoon-91807
04/24/2019, 6:26 PMlemon-spoon-91807
04/24/2019, 6:26 PMfoo(`${...} bar`)
lemon-spoon-91807
04/24/2019, 6:26 PM`${...} bar`
is just a normal interpolationlemon-spoon-91807
04/24/2019, 6:26 PMlemon-spoon-91807
04/24/2019, 6:27 PMfoo
the actual template pieces and args, you do foo`${...} bar`
lemon-spoon-91807
04/24/2019, 6:27 PMserviceName.apply(n => `${n} CPU`),
is also finelemon-spoon-91807
04/24/2019, 6:27 PMinterpolate
is doingfull-dress-10026
04/24/2019, 6:27 PMlemon-spoon-91807
04/24/2019, 6:27 PMlemon-spoon-91807
04/24/2019, 6:28 PMpulumi.interpolate`${cluster.name} - ${service.name}`
lemon-spoon-91807
04/24/2019, 6:28 PMpulumi.all([cluster.name, service.name]).apply(([cn, sn]) => `${sn} - {cn}`)
full-dress-10026
04/24/2019, 6:29 PMwidgetMatrix: [[r1c1 r1c2, ..., r1cN], [r2c1, r2c2, ... r2cN]]
full-dress-10026
04/24/2019, 6:30 PMlemon-spoon-91807
04/24/2019, 6:30 PMlemon-spoon-91807
04/24/2019, 6:31 PMlemon-spoon-91807
04/24/2019, 6:31 PMwidgets: [new RowWidget(r1c1, r1c2, ..., r1cN), new RowWidet(r2c1, r2c2, ... r2cN)]
lemon-spoon-91807
04/24/2019, 6:32 PMfull-dress-10026
04/24/2019, 6:32 PMlemon-spoon-91807
04/24/2019, 6:32 PMfull-dress-10026
04/24/2019, 6:33 PMlemon-spoon-91807
04/24/2019, 6:33 PMnew RowWidget(...list)
🙂lemon-spoon-91807
04/24/2019, 6:33 PMlemon-spoon-91807
04/24/2019, 6:33 PMfull-dress-10026
04/24/2019, 6:37 PMfunction rows(rs: awsx.cloudwatch.Widget[][]) {
return rs.map(cols => new awsx.cloudwatch.RowWidget(...cols))
}
full-dress-10026
04/24/2019, 6:37 PMlemon-spoon-91807
04/24/2019, 6:41 PMfull-dress-10026
04/24/2019, 7:50 PMRowWidget
with 5 LineGraphMetricWidget
and then several more row widgets with 2 LineGraphMetricWidget
each. Example:
[[r1c1, r1c2, r1c3, r1c4, r1c5]
[r2c1, r2c2]
[r3c1, r3c2]]
But in the CloudWatch UI, it renders like this:
[[r1c1, r1c2, r1c3, r1c4]
[r1c5, r2c1]
[r2c2, r3c1]
[r3c2]]
I would've expected this:
[[r1c1, r1c2, r1c3, r1c4]
[r1c5]
[r2c1, r2c2]
[r3c1, r3c2]]
lemon-spoon-91807
04/24/2019, 8:37 PMlemon-spoon-91807
04/24/2019, 8:37 PMfull-dress-10026
04/24/2019, 8:38 PMlemon-spoon-91807
04/24/2019, 8:39 PMlemon-spoon-91807
04/24/2019, 8:39 PMlemon-spoon-91807
04/24/2019, 8:39 PM[Dashboard]s are represented by a grid of columns 24 wide, with an unlimited number of rows.
lemon-spoon-91807
04/24/2019, 8:39 PMlemon-spoon-91807
04/24/2019, 8:39 PMlemon-spoon-91807
04/24/2019, 8:40 PMThe width of the widget in grid units (in a 24-column grid). The default is 6.
lemon-spoon-91807
04/24/2019, 8:40 PMlemon-spoon-91807
04/24/2019, 8:40 PMRepresents a horizontal sequence of [Widget]s in the [Dashboard]. Widgets are laid out horizontally in the grid until it would go past the max width of 24 columns. When that happens, the widgets will wrap to the next available grid row.
lemon-spoon-91807
04/24/2019, 8:40 PMlemon-spoon-91807
04/24/2019, 8:40 PMlemon-spoon-91807
04/24/2019, 8:41 PMfull-dress-10026
04/24/2019, 8:41 PMlemon-spoon-91807
04/24/2019, 8:41 PMlemon-spoon-91807
04/24/2019, 8:41 PMlemon-spoon-91807
04/24/2019, 8:41 PMfull-dress-10026
04/24/2019, 8:42 PMimport * as awsx from "@pulumi/awsx";
import * as pulumi from "@pulumi/pulumi";
interface DashArgs {
metricsStreamsService: awsx.ecs.FargateService
aggregateStreamsService: awsx.ecs.FargateService
modelExecutorService: awsx.ecs.FargateService
modelUpdaterService: awsx.ecs.FargateService
workloadMetricsWorkerService: awsx.ecs.FargateService
appIonsService: awsx.ecs.FargateService,
appIonsLb: awsx.elasticloadbalancingv2.ApplicationLoadBalancer
}
function fargateServiceCpuMemGraphs(service: awsx.ecs.FargateService) {
const serviceName = service.service.name;
return [
new awsx.cloudwatch.LineGraphMetricWidget({
title: pulumi.interpolate`${serviceName} CPU`,
metrics: [
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.cpuUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
}),
new awsx.cloudwatch.LineGraphMetricWidget({
title: pulumi.interpolate`${serviceName} Memory`,
metrics: [
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Minimum",
label: "min"
}),
awsx.ecs.metrics.memoryUtilization({
service: service,
statistic: "Maximum",
label: "max"
})
]
})
];
}
function rows(rs: awsx.cloudwatch.Widget[][]) {
return rs.map(cols => new awsx.cloudwatch.RowWidget(...cols))
}
export function createDashboard(env: string, dashboardArgs: DashArgs) {
// dashboardArgs.appIonsService.service.loadBalancers
new awsx.cloudwatch.Dashboard("cw-dashboard-" + env, {
name: `vm-scaler-${env}`,
widgets: rows([
fargateServiceCpuMemGraphs(dashboardArgs.appIonsService)
.concat([
new awsx.cloudwatch.LineGraphMetricWidget({
title: "HTTP Target Response Time",
metrics: [awsx.elasticloadbalancingv2.metrics.application.targetResponseTime(dashboardArgs.appIonsLb)]
}),
new awsx.cloudwatch.LineGraphMetricWidget({
title: "HTTP Requests",
metrics: [awsx.elasticloadbalancingv2.metrics.application.requestCount(dashboardArgs.appIonsLb)]
}),
new awsx.cloudwatch.LineGraphMetricWidget({
title: "HTTP 5XXs",
metrics: [awsx.elasticloadbalancingv2.metrics.application.httpCodeTarget5XXCount(dashboardArgs.appIonsLb)]
})]),
fargateServiceCpuMemGraphs(dashboardArgs.workloadMetricsWorkerService),
fargateServiceCpuMemGraphs(dashboardArgs.metricsStreamsService),
fargateServiceCpuMemGraphs(dashboardArgs.aggregateStreamsService),
fargateServiceCpuMemGraphs(dashboardArgs.modelExecutorService),
fargateServiceCpuMemGraphs(dashboardArgs.modelUpdaterService)
])
});
}
lemon-spoon-91807
04/24/2019, 8:44 PMlemon-spoon-91807
04/24/2019, 8:44 PMlemon-spoon-91807
04/24/2019, 8:44 PMlemon-spoon-91807
04/24/2019, 8:44 PMfull-dress-10026
04/24/2019, 8:45 PMlemon-spoon-91807
04/24/2019, 8:46 PMlemon-spoon-91807
04/24/2019, 8:46 PMlemon-spoon-91807
04/24/2019, 8:46 PMlemon-spoon-91807
04/24/2019, 8:46 PMlemon-spoon-91807
04/24/2019, 8:47 PMfull-dress-10026
04/24/2019, 8:50 PMlemon-spoon-91807
04/24/2019, 8:54 PMlemon-spoon-91807
04/24/2019, 8:56 PMlemon-spoon-91807
04/24/2019, 8:58 PMlemon-spoon-91807
04/24/2019, 8:58 PMlemon-spoon-91807
04/24/2019, 8:59 PMlemon-spoon-91807
04/24/2019, 8:59 PMlemon-spoon-91807
04/24/2019, 8:59 PMlemon-spoon-91807
04/25/2019, 3:09 AMlemon-spoon-91807
04/25/2019, 3:09 AMlemon-spoon-91807
04/25/2019, 3:09 AMlemon-spoon-91807
04/25/2019, 3:25 AMlemon-spoon-91807
04/25/2019, 3:25 AMlemon-spoon-91807
04/25/2019, 3:25 AMfull-dress-10026
04/25/2019, 5:05 PM