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

CW Agent configuration & EMF improvement #651

Merged
merged 2 commits into from
Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/syntax/compose_x/ecs.details/monitoring.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

.. meta::
:description: ECS Compose-X service level x-monitoring extensions
:keywords: AWS, AWS ECS, compose, monitoring

.. _x_services_monitoring_syntax:

======================
services.x-monitoring
======================

.. code-block:: yaml

services:
serviceA:
x-monitoring:
CWAgentCollectEmf: bool

Shorthands for monitoring features.


.. _monitoring_cw_agent_emf_collection:

CWAgentCollectEmf
===================

Simple boolean that will automatically add the CW Agent to the task definition and allow EMF Collection.
The ``AWS_EMF_AGENT_ENDPOINT`` environment variable for the other services is automatically set to point to the CW Agent.
A new SSM Parameter is created with the configuration necessary, and exposed to the container as ``CW_CONFIG_CONTENT``

See the `AWS CloudWatch agent & EMF Configuration for details`_ of what's configured under the hood.

.. _AWS CloudWatch agent & EMF Configuration for details: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Embedded_Metric_Format_Generation_CloudWatch_Agent.html
11 changes: 11 additions & 0 deletions docs/syntax/compose_x/ecs.details/prometheus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,23 @@ ContainersInsights Syntax Reference
.. code-block:: yaml

EnableCWAgentDebug: bool
CollectEmf: bool
CollectForAppMesh: bool
CollectForJavaJmx: bool|ExporterConfig
CollectForNginx: bool|ExporterConfig
AutoAddNginxPrometheusExporter: bool
CustomRules: [ExporterConfig]

CollectEmf
-------------

This allows to turn on EMF Collection from the CW Agent container.

.. hint::

Same as :ref:`monitoring_cw_agent_emf_collection`. See for more details.


CollectForAppMesh
-------------------

Expand Down
6 changes: 3 additions & 3 deletions ecs_composex/ecs/ecs_prometheus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ def add_cw_agent_to_family(
else:
prometheus_config = None
cw_agent_config = set_cw_config_parameter(family, collect_emf, **prometheus_options)
cw_agent_service = define_cloudwatch_agent(prometheus_config, cw_agent_config)
cw_agent_service = define_cloudwatch_agent(cw_agent_config, prometheus_config)
cw_agent_service.add_to_family(family, is_dependency=True)
set_ecs_cw_policy(family, prometheus_config, cw_agent_config)
set_ecs_cw_policy(family, cw_agent_config, prometheus_config)
family.cwagent_service = cw_agent_service
if collect_emf:
env_var = Environment(
Expand All @@ -118,5 +118,5 @@ def add_cw_agent_to_family(
)
family.iam_manager.add_new_managed_policy(
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
role_name=family.iam_manager.task_role._role_type,
role_name=family.iam_manager.task_role.role_type,
)
34 changes: 9 additions & 25 deletions ecs_composex/ecs/ecs_prometheus/config_ssm_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from yaml import Loader as Loader

from ecs_composex.common.cfn_params import STACK_ID_SHORT
from ecs_composex.common.troposphere_tools import add_resource
from ecs_composex.ecs import ecs_params
from ecs_composex.ecs.ecs_prometheus.emf_processors import generate_emf_processors

Expand Down Expand Up @@ -69,24 +70,16 @@ def set_cw_prometheus_config_parameter(
}

parameter = SSMParameter(
f"{family.logical_name}SSMPrometheusConfig",
f"{family.logical_name}CWAgentPrometheusScrapingConfig",
Tier="Standard",
Type="String",
Name=Sub(
f"/ecs/config/prometheus/${{{ecs_params.CLUSTER_NAME.title}}}/${{STACK_SHORT_ID}}"
f"/${{{ecs_params.SERVICE_NAME.title}}}",
STACK_SHORT_ID=STACK_ID_SHORT,
),
Description=Sub(
f"Prometheus Scraping SSM Parameter for ECS Cluster: ${{{ecs_params.CLUSTER_NAME.title}}}"
"CW Agent Prometheus Scraping config for "
f"ecs/${{{ecs_params.CLUSTER_NAME.title}}}/${{{ecs_params.SERVICE_NAME.title}}}"
),
Value=Sub(yaml.dump(value_py, Dumper=Dumper), STACK_SHORT_ID=STACK_ID_SHORT),
)
if parameter.title not in family.template.resources:
family.template.add_resource(parameter)
return parameter
else:
return family.template.resources[parameter.title]
return add_resource(family.template, parameter)


def set_cw_config_parameter(
Expand Down Expand Up @@ -131,24 +124,15 @@ def set_cw_config_parameter(
"emf_processor"
] = emf_processors
parameter = SSMParameter(
f"{family.logical_name}SSMCWAgentPrometheusConfig",
f"{family.logical_name}SsmCWAgentConfig",
Tier="Intelligent-Tiering",
Type="String",
Name=Sub(
f"/ecs/config/cw_agent_config/${{{ecs_params.CLUSTER_NAME.title}}}/${{STACK_SHORT_ID}}"
f"/${{{ecs_params.SERVICE_NAME.title}}}",
STACK_SHORT_ID=STACK_ID_SHORT,
),
Description=Sub(
f"Prometheus Scraping SSM Parameter for ECS Cluster: ${{{ecs_params.CLUSTER_NAME.title}}}"
f"CW Agent config for ecs/${{{ecs_params.CLUSTER_NAME.title}}}/${{{ecs_params.SERVICE_NAME.title}}}"
),
Value=Sub(
json.dumps(value_py, ensure_ascii=True, sort_keys=True, indent=2),
json.dumps(value_py, ensure_ascii=True, sort_keys=True),
STACK_SHORT_ID=STACK_ID_SHORT,
),
)
if parameter.title not in family.template.resources:
family.template.add_resource(parameter)
return parameter
else:
return family.template.resources[parameter.title]
return add_resource(family.template, parameter)
35 changes: 23 additions & 12 deletions ecs_composex/ecs/ecs_prometheus/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
from ecs_composex.ecs import ecs_params


def define_cloudwatch_agent(cw_prometheus_config, cw_agent_config) -> ManagedSidecar:
def define_cloudwatch_agent(
cw_agent_config, cw_prometheus_config=None
) -> ManagedSidecar:
"""
Function to define the CW Agent image task definition

:param cw_prometheus_config:
:param cw_agent_config:
:param cw_prometheus_config:
:return:
"""
from copy import deepcopy
Expand All @@ -40,7 +42,7 @@ def define_cloudwatch_agent(cw_prometheus_config, cw_agent_config) -> ManagedSid
Name="CW_CONFIG_CONTENT",
ValueFrom=Sub(
f"arn:${{{AWS_PARTITION}}}:ssm:${{{AWS_REGION}}}:${{{AWS_ACCOUNT_ID}}}"
f":parameter${{{cw_agent_config.title}}}"
f":parameter/${{{cw_agent_config.title}}}"
),
),
]
Expand All @@ -50,7 +52,7 @@ def define_cloudwatch_agent(cw_prometheus_config, cw_agent_config) -> ManagedSid
Name="PROMETHEUS_CONFIG_CONTENT",
ValueFrom=Sub(
f"arn:${{{AWS_PARTITION}}}:ssm:${{{AWS_REGION}}}:${{{AWS_ACCOUNT_ID}}}"
f":parameter${{{cw_prometheus_config.title}}}"
f":parameter/${{{cw_prometheus_config.title}}}"
),
),
)
Expand All @@ -67,8 +69,8 @@ def define_cloudwatch_agent(cw_prometheus_config, cw_agent_config) -> ManagedSid

def set_ecs_cw_policy(
family: ComposeFamily,
prometheus_parameter: Parameter,
cw_config_parameter: Parameter,
prometheus_parameter: Parameter = None,
) -> None:
"""
Renders the IAM policy to grant the TaskRole access to CW, ECS and SSM Parameters
Expand All @@ -83,6 +85,20 @@ def set_ecs_cw_policy(
PolicyDocument={
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CWAgentConfigurationFromSSMParameter",
"Effect": "Allow",
"Action": ["ssm:GetParameter*"],
"Resource": [
Sub(
"arn:aws:ssm:*:${AWS::AccountId}:parameter/AmazonCloudWatch-*"
),
Sub(
f"arn:${{{AWS_PARTITION}}}:ssm:${{{AWS_REGION}}}:${{{AWS_ACCOUNT_ID}}}"
f":parameter/${{{cw_config_parameter.title}}}"
),
],
},
{
"Sid": "EnableCreationAndManagementOfContainerInsightsLogEvents",
"Effect": "Allow",
Expand Down Expand Up @@ -142,18 +158,13 @@ def set_ecs_cw_policy(
if prometheus_parameter:
ecs_sd_policy.PolicyDocument["Statement"].append(
{
"Sid": "ExtractFromCloudWatchAgentServerPolicy",
"Sid": "CWAgentPrometheusScrapingConfigurationAccess",
"Effect": "Allow",
"Action": ["ssm:GetParameter*"],
"Resource": [
Sub("arn:aws:ssm:*:${AWS::AccountId}:parameter/AmazonCloudWatch-*"),
Sub(
f"arn:${{{AWS_PARTITION}}}:ssm:${{{AWS_REGION}}}:${{{AWS_ACCOUNT_ID}}}"
f":parameter${{{prometheus_parameter.title}}}"
),
Sub(
f"arn:${{{AWS_PARTITION}}}:ssm:${{{AWS_REGION}}}:${{{AWS_ACCOUNT_ID}}}"
f":parameter${{{cw_config_parameter.title}}}"
f":parameter/${{{prometheus_parameter.title}}}"
),
],
},
Expand Down
5 changes: 4 additions & 1 deletion ecs_composex/ecs/managed_sidecars/aws_cw_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
CW_AGENT_NAME = "cloudwatch-agent"
CW_AGENT_DEFINITION = {
"image": CW_IMAGE_PARAMETER.Default,
"ports": [{"target": 25888, "protocol": "tcp"}],
"ports": [
{"target": 25888, "protocol": "tcp"},
{"target": 25888, "protocol": "udp"},
],
"deploy": {
"resources": {"limits": {"cpus": 0.1, "memory": "256M"}},
},
Expand Down
27 changes: 21 additions & 6 deletions ecs_composex/ecs/task_iam/task_role.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# SPDX-License-Identifier: MPL-2.0
# Copyright 2020-2022 John Mille <[email protected]>

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from ecs_composex.ecs.ecs_family import ComposeFamily

from compose_x_common.compose_x_common import keyisset
from troposphere import GetAtt, Output, Ref, Sub
Expand All @@ -18,15 +24,12 @@ class EcsRole:
Class to wrap around the AWS IAM Role
"""

def __init__(self, family, role_type):
def __init__(self, family: ComposeFamily, role_type: str):
"""
:param family: The family the role will belong to
"""
if role_type not in [TASK_ROLE_T, EXEC_ROLE_T]:
raise ValueError(
"role_type is", role_type, "expected one of", [TASK_ROLE_T, EXEC_ROLE_T]
)
self._role_type = role_type
self._role_type = None
self.role_type = role_type
self._name = None
self._arn = None
self.family = family
Expand Down Expand Up @@ -55,6 +58,18 @@ def __init__(self, family, role_type):
self.outputs = []
self.lookup = {}

@property
def role_type(self) -> str:
return self._role_type

@role_type.setter
def role_type(self, role_type: str) -> None:
if role_type not in [TASK_ROLE_T, EXEC_ROLE_T]:
raise ValueError(
"role_type is", role_type, "expected one of", [TASK_ROLE_T, EXEC_ROLE_T]
)
self._role_type = role_type

@property
def name_param(self):
"""
Expand Down
3 changes: 3 additions & 0 deletions ecs_composex/specs/compose-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@
"x-prometheus": {
"$ref": "services.x-prometheus.spec.json"
},
"x-monitoring": {
"$ref": "services.x-monitoring.spec.json"
},
"x-ecs": {
"$ref": "services.x-ecs.spec.json"
},
Expand Down
15 changes: 15 additions & 0 deletions ecs_composex/specs/services.x-monitoring.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"id": "services.x-monitoring",
"$id": "services.x-monitoring.spec.json",
"type": "object",
"title": "services.x-monitoring specification",
"description": "The services.x-monitoring specification for ECS Compose-X",
"additionalProperties": false,
"properties": {
"CWAgentCollectEmf": {
"type": "boolean"
}
},
"definitions": {}
}