How do I create an AWS App Mesh using AWS CDK - amazon-web-services

I am attempting to create a stack for (currently) 9 .NET Core microservices to run in ECS Fargate and communicate with each other via App Mesh. I plan on creating an Infrastructure stack which creates the App Mesh resource and the ECS Cluster and a Microservice stack that creates the resources for each service and adds them to the App Mesh and ECS cluster.
I currently have this code:
Vpc = Amazon.CDK.AWS.EC2.Vpc.FromLookup(this, "vpc", new VpcLookupOptions
{
VpcId = "xxxxxxxxxxxx"
});
DefaultCloudMapNamespace = new CloudMapNamespaceOptions
{
Vpc = Vpc,
Name = dnsNamespace,
Type = NamespaceType.DNS_PRIVATE,
};
EcsCluster = new Cluster(this, $"{Env}-linux-cluster", new ClusterProps
{
Vpc = Vpc,
ClusterName = $"{Env}-linux-cluster",
DefaultCloudMapNamespace = DefaultCloudMapNamespace
});
This seems to be okay - it creates a hosted zone in Route53.
When I am creating the Service for Cloud Map, I'm using this code:
var cloudMapService = new Service(this, serviceName, new ServiceProps
{
Namespace = new PrivateDnsNamespace(this, $"{serviceNameHyphen}-cm-namespace", new PrivateDnsNamespaceProps
{
Vpc = infrastructureStack.Vpc,
Name = $"{serviceName}.dev",
}),
DnsRecordType = DnsRecordType.SRV,
DnsTtl = Duration.Seconds(60),
RoutingPolicy = RoutingPolicy.MULTIVALUE,
Name = serviceName
});
This is the first time I'm working with App Mesh & Cloud Map, but I would expect to use the same private hosted zone for both the Cloud Map namespace and the Cloud Map Service namespace.
Is this the correct approach?

My approach:
I create Namespace first
cloud_map = sds.PrivateDnsNamespace(
self,
"PrivateNameSpace",
vpc=vpcObject,
description=' '.join(["Private DNS for", self.node.try_get_context('EnvironmentName')]),
name=service_domain
)
Then when create Virtual Service I use same domain for it
vservice = mesh.VirtualService(
self,
"VirtualService",
virtual_service_name='.'.join([node_name, service_domain]),
virtual_service_provider=mesh.VirtualServiceProvider.virtual_node(vnode)
)
Then call it when create ECS service
ecs_service = ecs.Ec2Service(
self,
"ECSService",
task_definition=ecs_task,
placement_strategies=[
ecs.PlacementStrategy.spread_across_instances()
],
desired_count=desiredCount,
cluster=clusterObject,
security_groups=[sgObject],
vpc_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PRIVATE
),
enable_ecs_managed_tags=True,
health_check_grace_period=cdk.Duration.seconds(120),
max_healthy_percent=200,
min_healthy_percent=50,
cloud_map_options=ecs.CloudMapOptions(
cloud_map_namespace=cloud_map,
dns_record_type=cm.DnsRecordType.A,
dns_ttl=cdk.Duration.seconds(300),
failure_threshold=1,
name=node_name
),
)

Related

Declare custom DNS name for Fargate using CDK

I am creating a C# CDK stack that deploys both a Fargate service and an Elastic Beanstalk service.
Both these services will have a custom domain pointing to them using a third party domain service (i.e. non AWS).
For elastic beanstalk, I can simply set the CnamePrefix below, and AWS will generate something like http://beanstalk-dev.ap-southeast-2.elasticbeanstalk.com/ which I can then point my custom domain to (after adding some listeners to the load balancer).
var elasticBeanstalkEnv = new CfnEnvironment(this, "ElbsEnv", new CfnEnvironmentProps
{
EnvironmentName = "beanstalk-dev",
ApplicationName = appName,
SolutionStackName = "64bit Amazon Linux 2 v2.4.1 running .NET Core",
OptionSettings = optionSettingProperties,
VersionLabel = version.Ref,
CnamePrefix = "beanstalk-dev", <--- This property
Tier = new CfnEnvironment.TierProperty
{
Name = "WebServer",
Type = "Standard"
}
});
I am also trying to do something similar with Fargate. But I can not find any settings like the one for elastic beanstalk.
Below is what I have so far, which will deploy a DNS name like LB-71455902.ap-southeast-2.elb.amazonaws.com
var fargate = new ApplicationLoadBalancedFargateService(this, "reportviewer-server",
new ApplicationLoadBalancedFargateServiceProps
{
TaskImageOptions = new ApplicationLoadBalancedTaskImageOptions
{
Image = ContainerImage.FromRegistry("myImage",
new RepositoryImageProps { Credentials = mycreds})
},
PublicLoadBalancer = true,
LoadBalancerName = "LB",
ServiceName = "frontend",
RecordType = ApplicationLoadBalancedServiceRecordType.CNAME
});
fargate.LoadBalancer.AddListener("ecs-https-listener", new ApplicationListenerProps
{
SslPolicy = SslPolicy.RECOMMENDED,
Protocol = ApplicationProtocol.HTTPS,
Open = true,
Port = 443,
Certificates = new IListenerCertificate[]
{
ListenerCertificate.FromArn(
"myArn")
},
DefaultTargetGroups = new IApplicationTargetGroup[]
{
fargate.TargetGroup
}
});
How would I setup my stack to create a "static" DNS name that wont be different each time I create/destroy my stack?

Using Terraform, how would I create a AWS Kubernetes cluster with Fargate?

I am looking for a recipe using Terraform to create a Kubernetes cluster on AWS using Fargate. I cannot find any end-to-end documentation to do this.
I am using SSO, and so terraform needs to use my AWS credentials to do this.
No example I can find addresses using AWS credentials and Fargate.
If anyone has done this and has a recipe for all of the above, please share.
You can use popular module for that
terraform-aws-eks. It supports Fargate EKS as well. Since its open sourced, you can also have a look at exactly how to create such clusters if you want to fork and customize the module, or create your own scratch.
Example use for Fargate EKS from its docs:
module "eks" {
source = "../.."
cluster_name = local.cluster_name
cluster_version = "1.17"
subnets = module.vpc.private_subnets
tags = {
Environment = "test"
GithubRepo = "terraform-aws-eks"
GithubOrg = "terraform-aws-modules"
}
vpc_id = module.vpc.vpc_id
fargate_profiles = {
example = {
namespace = "default"
# Kubernetes labels for selection
# labels = {
# Environment = "test"
# GithubRepo = "terraform-aws-eks"
# GithubOrg = "terraform-aws-modules"
# }
# using specific subnets instead of all the ones configured in eks
# subnets = ["subnet-0ca3e3d1234a56c78"]
tags = {
Owner = "test"
}
}
}
map_roles = var.map_roles
map_users = var.map_users
map_accounts = var.map_accounts
}

Can you create a route 53 A Record that maps directly to the IP address of a ecs service and ecs task defined using the AWS CDK?

Can you create a route 53 A Record that maps directly to the IP address of a ecs service and ecs task defined using the AWS CDK?
I have the following code
FargateTaskDefinition taskDef = new FargateTaskDefinition(this, "DevStackTaskDef", new FargateTaskDefinitionProps()
{
MemoryLimitMiB = 2048,
Cpu = 512
});
var service = new FargateService(this, "DevStackFargateService", new FargateServiceProps()
{
ServiceName = "DevStackFargateService",
TaskDefinition = taskDef,
Cluster = cluster,
DesiredCount = 1,
SecurityGroup = securityGroup,
AssignPublicIp = true,
VpcSubnets = new SubnetSelection()
{
SubnetType = SubnetType.PUBLIC
}
});
new ARecord(this, "AliasRecord", new ARecordProps()
{
Zone = zone,
Target = RecordTarget.FromIpAddresses() //here is the line in question.
});
The ARecordProps.Target value is the one I'm stuck on. I can not find a way to get the ip address of the task that will be created. Does any one know if this is possible to do? I would really like to avoid using load balancers as this is a dev/test environment. I have also looked at the aws-route53-targets module and see that it only supports
ApiGateway
ApiGatewayDomain
BucketWebsiteTarget
ClassicLoadBalancerTarget
CloudFrontTarget
LoadBalancerTarget
Any help would be much appreciated. Thanks

Terraform: Create AWS Lightsail instance from snapshot

Given the Terraform documentation on AWS Lightsail, I can construct a brand new Lightsail instance as follows.
resource "aws_lightsail_instance" "my_ls_instance" {
name = "my_ls"
availability_zone = "us-east-1b"
blueprint_id = "ubuntu_18_04"
bundle_id = "2xlarge_2_0"
key_pair_name = "MyKeyName"
}
It is possible to create a Lightsail instance from a Lightsail snapshot using Terraform?
No, it's not. Right now Terraform can only create instances based on Lightsail blueprints.
You can, however create an instance from snapshot in python3 /w boto3. Let me include my code:
#######
import boto3
client = boto3.client('lightsail')
response = client.create_instances_from_snapshot(
instanceNames=[
'myitblog',
],
availabilityZone='us-east-1a',
instanceSnapshotName='MYITBLOG_https',
bundleId='nano_2_0',
)
response = client.attach_static_ip(
staticIpName='StaticIp-1',
instanceName='myitblog'
)

How to create RFC 2782-compliant SRV record with Terraform and AWS Service Discovery?

I'm running a MySQL database in AWS ECS (can't use RDS), and I'd like to use ECS Service Discovery to populate a SRV record which points to that database. I'm using Terraform to configure all AWS services.
This is what I have working so far ...
resource "aws_service_discovery_service" "mysqldb" {
name = "mysqldb"
health_check_custom_config {
failure_threshold = 3
}
dns_config {
namespace_id = "${aws_service_discovery_private_dns_namespace.stack_local.id}"
dns_records {
ttl = "300"
type = "SRV"
}
routing_policy = "MULTIVALUE"
}
}
resource "aws_service_discovery_private_dns_namespace" "stack_local" {
name = "${var.stack_name}.local"
description = "Testing with AWS Service Discovery"
vpc = "${aws_vpc.vpc.id}"
}
However, this creates a SRV record (mysqldb.stack_name.local.) that is not RFC 2782-compliant. In order to be compliant, it should include the service name and protocol, like this: _mysql_._tcp.mysqldb.stack_name.local.
I tried changing the name to something like _mysql._tcp.mysqldb but that failed because AWS requires the name to be a single label without any . in it.
Is it possible to create an RFC 2782-compliant SRV record using ECS Service Discovery with Terraform and the Terraform AWS provider?