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

Update app.py #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
251 changes: 244 additions & 7 deletions cdk/app.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,257 @@
#!/usr/bin/env python3

import os

import aws_cdk as cdk
from constructs import Construct
from aws_cdk import (
App,
Environment,
App, CfnOutput, Stack, Environment, Fn, Duration,
aws_ec2,
aws_ecs,
aws_ecs_patterns as ecs_patterns,
aws_iam,
aws_servicediscovery,
aws_appmesh,
aws_logs
)

from os import getenv
from cdk.nodejsservice import NodejsService

account = os.getenv('AWS_ACCOUNT_ID')
region = os.getenv('AWS_DEFAULT_REGION')
stack_name = "ecsworkshop-nodejs"
env = Environment(account=account, region=region)
_env = Environment(account=getenv('AWS_ACCOUNT_ID'), region=getenv('AWS_DEFAULT_REGION'))
environment = "ecsworkshop"
stack_name = "{}-nodejs".format(environment)

app = App()
NodejsService(app, stack_name, env=env)
# Creating a construct that will populate the required objects created in the platform repo such as vpc, ecs cluster, and service discovery namespace
class BasePlatform(Construct):

def __init__(self, scope: Construct, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
self.environment_name = 'ecsworkshop'

# The base platform stack is where the VPC was created, so all we need is the name to do a lookup and import it into this stack for use
self.vpc = aws_ec2.Vpc.from_lookup(
self, "VPC",
vpc_name='{}-base/BaseVPC'.format(self.environment_name)
)

self.sd_namespace = aws_servicediscovery.PrivateDnsNamespace.from_private_dns_namespace_attributes(
self, "SDNamespace",
namespace_name=cdk.Fn.import_value('NSNAME'),
namespace_arn=cdk.Fn.import_value('NSARN'),
namespace_id=cdk.Fn.import_value('NSID')
)

self.ecs_cluster = aws_ecs.Cluster.from_cluster_attributes(
self, "ECSCluster",
cluster_name=cdk.Fn.import_value('ECSClusterName'),
security_groups=[],
vpc=self.vpc,
default_cloud_map_namespace=self.sd_namespace
)

self.services_sec_grp = aws_ec2.SecurityGroup.from_security_group_id(
self, "ServicesSecGrp",
security_group_id=cdk.Fn.import_value('ServicesSecGrp')
)


class NodejsService(Stack):

def __init__(self, scope: Stack, id: str, **kwargs):
super().__init__(scope, id, **kwargs)

self.base_platform = BasePlatform(self, self.stack_name)

self.fargate_task_def = aws_ecs.TaskDefinition(
self, "TaskDef",
compatibility=aws_ecs.Compatibility.EC2_AND_FARGATE,
cpu='256',
memory_mib='512',
#appmesh-proxy-uncomment
proxy_configuration=aws_ecs.AppMeshProxyConfiguration(
container_name="envoy", #App Mesh side card name that will proxy the requests
properties=aws_ecs.AppMeshProxyConfigurationProps(
app_ports=[3000],
proxy_ingress_port=15000,
proxy_egress_port=15001,
egress_ignored_i_ps=["169.254.170.2","169.254.169.254"],
ignored_uid=1337
)
)
#appmesh-proxy-uncomment
)

self.logGroup = aws_logs.LogGroup(self,"ecsworkshopNodejs",
# log_group_name="ecsworkshop-nodejs",
retention=aws_logs.RetentionDays.ONE_WEEK
)

self.container = self.fargate_task_def.add_container(
"NodeServiceContainerDef",
image=aws_ecs.ContainerImage.from_registry("public.ecr.aws/aws-containers/ecsdemo-nodejs"),
memory_reservation_mib=128,
logging=aws_ecs.LogDriver.aws_logs(
stream_prefix='/nodejs-container',
log_group=self.logGroup
),
environment={
"REGION": getenv('AWS_DEFAULT_REGION')
},
container_name="nodejs-app"
)

self.container.add_port_mappings(
aws_ecs.PortMapping(
container_port=3000
)
)

self.fargate_service = aws_ecs.FargateService(
self, "NodejsFargateService",
service_name='ecsdemo-nodejs',
task_definition=self.fargate_task_def,
cluster=self.base_platform.ecs_cluster,
security_groups=[self.base_platform.services_sec_grp],
desired_count=1,
cloud_map_options=aws_ecs.CloudMapOptions(
cloud_map_namespace=self.base_platform.sd_namespace,
name='ecsdemo-nodejs'
)
)
self.fargate_task_def.add_to_task_role_policy(
aws_iam.PolicyStatement(
actions=['ec2:DescribeSubnets'],
resources=['*']
)
)


appmesh(self)

def appmesh(self):


# Importing app mesh service
self.mesh = aws_appmesh.Mesh.from_mesh_arn(
self,
"EcsWorkShop-AppMesh",
mesh_arn=cdk.Fn.import_value("MeshArn")
)

# Importing App Mesh virtual gateway
self.mesh_vgw = aws_appmesh.VirtualGateway.from_virtual_gateway_attributes(
self,
"Mesh-VGW",
mesh=self.mesh,
virtual_gateway_name=cdk.Fn.import_value("MeshVGWName")
)

# App Mesh virtual node configuration
self.mesh_nodejs_vn = aws_appmesh.VirtualNode(
self,
"MeshNodeJsNode",
mesh=self.mesh,
virtual_node_name="nodejs",
listeners=[aws_appmesh.VirtualNodeListener.http(port=3000)],
service_discovery=aws_appmesh.ServiceDiscovery.cloud_map(self.fargate_service.cloud_map_service),
access_log=aws_appmesh.AccessLog.from_file_path("/dev/stdout")
)

# App Mesh envoy proxy container configuration
self.envoy_container = self.fargate_task_def.add_container(
"NodeJsServiceProxyContdef",
image=aws_ecs.ContainerImage.from_registry("public.ecr.aws/appmesh/aws-appmesh-envoy:v1.18.3.0-prod"),
container_name="envoy",
memory_reservation_mib=128,
environment={
"REGION": getenv('AWS_DEFAULT_REGION'),
"ENVOY_LOG_LEVEL": "trace",
"ENABLE_ENVOY_STATS_TAGS": "1",
"ENABLE_ENVOY_XRAY_TRACING": "1",
"APPMESH_RESOURCE_ARN": self.mesh_nodejs_vn.virtual_node_arn
},
essential=True,
logging=aws_ecs.LogDriver.aws_logs(
stream_prefix='/mesh-envoy-container',
log_group=self.logGroup
),
health_check=aws_ecs.HealthCheck(
interval=cdk.Duration.seconds(5),
timeout=cdk.Duration.seconds(10),
retries=10,
command=["CMD-SHELL","curl -s http://localhost:9901/server_info | grep state | grep -q LIVE"],
),
user="1337"
)

self.envoy_container.add_ulimits(aws_ecs.Ulimit(
hard_limit=15000,
name=aws_ecs.UlimitName.NOFILE,
soft_limit=15000
)
)

# Primary container needs to depend on envoy before it can be reached out
self.container.add_container_dependencies(aws_ecs.ContainerDependency(
container=self.envoy_container,
condition=aws_ecs.ContainerDependencyCondition.HEALTHY
)
)

# Enable app mesh Xray observability
#ammmesh-xray-uncomment
#self.xray_container = self.fargate_task_def.add_container(
# "NodeJsServiceXrayContdef",
# image=aws_ecs.ContainerImage.from_registry("amazon/aws-xray-daemon"),
# logging=aws_ecs.LogDriver.aws_logs(
# stream_prefix='/xray-container',
# log_group=self.logGroup
# ),
# essential=True,
# container_name="xray",
# memory_reservation_mib=256,
# user="1337"
#)

#self.envoy_container.add_container_dependencies(aws_ecs.ContainerDependency(
# container=self.xray_container,
# condition=aws_ecs.ContainerDependencyCondition.START
# )
#)
#ammmesh-xray-uncomment
self.fargate_task_def.add_to_task_role_policy(
aws_iam.PolicyStatement(
actions=['ec2:DescribeSubnets'],
resources=['*']
)
)

self.fargate_service.connections.allow_from_any_ipv4(
port_range=aws_ec2.Port(protocol=aws_ec2.Protocol.TCP, string_representation="tcp_3000", from_port=3000, to_port=3000),
description="Allow TCP connections on port 3000"
)

# Adding policies to work with observability (xray and cloudwath)
self.fargate_task_def.execution_role.add_managed_policy(aws_iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEC2ContainerRegistryReadOnly"))
self.fargate_task_def.execution_role.add_managed_policy(aws_iam.ManagedPolicy.from_aws_managed_policy_name("CloudWatchLogsFullAccess"))
self.fargate_task_def.task_role.add_managed_policy(aws_iam.ManagedPolicy.from_aws_managed_policy_name("CloudWatchFullAccess"))
self.fargate_task_def.task_role.add_managed_policy(aws_iam.ManagedPolicy.from_aws_managed_policy_name("AWSXRayDaemonWriteAccess"))
self.fargate_task_def.task_role.add_managed_policy(aws_iam.ManagedPolicy.from_aws_managed_policy_name("AWSAppMeshEnvoyAccess"))


# Adding mesh virtual service
self.mesh_nodejs_vs = aws_appmesh.VirtualService(self,"mesh-nodejs-vs",
virtual_service_provider=aws_appmesh.VirtualServiceProvider.virtual_node(self.mesh_nodejs_vn),
virtual_service_name="{}.{}".format(self.fargate_service.cloud_map_service.service_name,self.fargate_service.cloud_map_service.namespace.namespace_name)
)

# Exporting CF (outputs) to make references from other cdk projects.
CfnOutput(self,"MeshNodejsVSARN",value=self.mesh_nodejs_vs.virtual_service_arn,export_name="MeshNodejsVSARN")
CfnOutput(self,"MeshNodeJsVSName",value=self.mesh_nodejs_vs.virtual_service_name,export_name="MeshNodeJsVSName")


app = App()
NodejsService(app, stack_name, env=_env)
app.synth()