<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Constantin</title>
    <description>The latest articles on DEV Community by Constantin (@jaecktec).</description>
    <link>https://dev.to/jaecktec</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F516141%2F0027c2f7-52f3-43f3-9833-17df63bfc1d4.png</url>
      <title>DEV Community: Constantin</title>
      <link>https://dev.to/jaecktec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaecktec"/>
    <language>en</language>
    <item>
      <title>aws app mesh with cdk in 5 steps</title>
      <dc:creator>Constantin</dc:creator>
      <pubDate>Thu, 16 Sep 2021 12:50:05 +0000</pubDate>
      <link>https://dev.to/jaecktec/aws-app-mesh-in-5-steps-1bmc</link>
      <guid>https://dev.to/jaecktec/aws-app-mesh-in-5-steps-1bmc</guid>
      <description>&lt;h3&gt;
  
  
  note:
&lt;/h3&gt;

&lt;p&gt;if you want to skip reading and just want to see the finished code visit my &lt;a href="https://github.com/jaecktec/aws-cdk-appmesh-example/tree/final" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;docker installed&lt;/li&gt;
&lt;li&gt;node and npm installed&lt;/li&gt;
&lt;li&gt;AWS account&lt;/li&gt;
&lt;li&gt;a few spare bucks (AWS is not for free)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  what's fargate
&lt;/h3&gt;

&lt;p&gt;I'm not copy-pasting the marketing description here. What is interesting in the end is how it affects your day-to-day job. &lt;br&gt;
The most important features are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;host your Docker images at ease&lt;/li&gt;
&lt;li&gt;utilize AWS cloud-native features &lt;/li&gt;
&lt;li&gt;don't waste time in managing EC2 instances &lt;/li&gt;
&lt;li&gt;quick deployment and advanced rollout features when using &lt;a href="https://aws.amazon.com/de/codedeploy/" rel="noopener noreferrer"&gt;aws-code-deploy&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  what does a service mesh for you:
&lt;/h3&gt;

&lt;p&gt;A service mesh helps you with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;service discovery (routing between Microservices), &lt;/li&gt;
&lt;li&gt;streamlined logging and tracing between Microservices&lt;/li&gt;
&lt;li&gt;help with resilience by routing traffic away from failed instances (usually you'd implement this with a load balancer). &lt;/li&gt;
&lt;li&gt;enables you to easily implement features like canary deployments and blue/green testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  how does it do that?
&lt;/h3&gt;

&lt;p&gt;These features are archived by deploying a managed proxy alongside your application which intercepts traffic from and to your application. The proxy takes care of fetching the mesh configuration, routing the requests, and registering your instance to the mesh. The deployment is often referred to as 'sidecar' since you have one proxy per service instance. &lt;/p&gt;
&lt;h3&gt;
  
  
  how is it supposed to work in amazon
&lt;/h3&gt;

&lt;p&gt;Amazon supports Service-Meshes by giving you access to a managed &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;envoy-proxy&lt;/a&gt; hosting.&lt;br&gt;&lt;br&gt;
What you need to do: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you deploy an envoy proxy as a side-car image next to your application container. &lt;/li&gt;
&lt;li&gt;you utilize the fargate-proxy config and reroute traffic to the envoy-proxy&lt;/li&gt;
&lt;li&gt;you define virtual services/gateways/routes to stitch your mesh together
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  terminology in AWS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;virtual node:&lt;/strong&gt; represents a compute instance like a fargate container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;virtual service:&lt;/strong&gt; represents a logical group (1..n) of nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;virtual route:&lt;/strong&gt; uses parameter (HTTP/GRPC) to route traffic to specific nodes/services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;virtual gateway:&lt;/strong&gt; specifies an incoming or outgoing gateway from app-mesh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AWS Showcase: &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd1.awsstatic.com%2Fdiagrams%2Fimage%2520%289%29.c86b0113dde0d2dbdc99a1ffad59805d86b5cb82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd1.awsstatic.com%2Fdiagrams%2Fimage%2520%289%29.c86b0113dde0d2dbdc99a1ffad59805d86b5cb82.png" alt="Aws showcase"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  resources
&lt;/h3&gt;

&lt;p&gt;some great resources I used to build my service mesh are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/topics/microservices/what-is-a-service-mesh" rel="noopener noreferrer"&gt;What's a service mesh?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/qM4uf9l5lus" rel="noopener noreferrer"&gt;AWS Cloud Containers Conference - Deep Dive on Configuring AWS App Mesh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step 1, preparation.
&lt;/h2&gt;

&lt;p&gt;I've prepared two microservices with a small react app that polls two endpoints. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7tkxtb17402noiyvevz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7tkxtb17402noiyvevz.png" alt="BFF Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;let's clone the GitHub repository&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@github.com:jaecktec/aws-cdk-appmesh-example.git
cd aws-cdk-appmesh-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quick walkthrough:&lt;/p&gt;

&lt;h3&gt;
  
  
  infrastructure folder
&lt;/h3&gt;

&lt;p&gt;contains AWS CDK infrastructure such as: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fargate cluster, services and task-defs&lt;/li&gt;
&lt;li&gt;private DNS namespace&lt;/li&gt;
&lt;li&gt;ALB
used to host a small web app that accesses a non-exposed microservice through a gateway. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  services
&lt;/h3&gt;

&lt;p&gt;contains some microservices alongside Dockerfiles&lt;/p&gt;

&lt;h3&gt;
  
  
  services/color-teller-backend
&lt;/h3&gt;

&lt;p&gt;rudimentary expressjs service which returns the environment variable 'COLOR' on [GET]/color and 200 on [GET]/health&lt;/p&gt;

&lt;h3&gt;
  
  
  services/color-teller-client
&lt;/h3&gt;

&lt;p&gt;small react-app whith polls /gateway/color/color and /version and a expressjs services that exposes two endpoints, one /gateway/color/* which is a http-proxy to an endpoint defined in an env-variable called &lt;code&gt;COLOR_BACKEND&lt;/code&gt; + a version endpoint which returns the env-variable called &lt;code&gt;VERSION&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  test your prerequisites
&lt;/h3&gt;

&lt;p&gt;open the infrastructure folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd infrastructure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i
npx cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this might take a few seconds, once you're asked to confirm, confirm by entering &lt;code&gt;y&lt;/code&gt; and hit enter.&lt;/p&gt;

&lt;p&gt;once everything got deployed you should see an HTTP address, open this in the browser. If everything worked you should see a &lt;code&gt;Color Teller&lt;/code&gt; showing &lt;code&gt;vanilla red&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeaw8kt0evraloq6oslv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feeaw8kt0evraloq6oslv.png" alt="Sample image of deployed app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;to not hit conflicts, let's clean up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cdk destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free to browse through the code but please don't judge me on the microservices, they were hacked together in a local coffee shop (not ⭐️🪣, I'm not that wealthy)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: create mesh
&lt;/h3&gt;

&lt;p&gt;In our first step, we will create our &lt;code&gt;Mesh&lt;/code&gt; and modify &lt;code&gt;createService&lt;/code&gt; to accept and use the mesh. &lt;/p&gt;

&lt;p&gt;before the first &lt;code&gt;createService&lt;/code&gt; call add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const mesh = new Mesh(this, 'mesh');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this will create an app-mesh for us&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: create a virtual node
&lt;/h3&gt;

&lt;p&gt;now modify the signature of &lt;code&gt;createService&lt;/code&gt; to accept a &lt;code&gt;Mesh&lt;/code&gt; and return a &lt;code&gt;VirtualNode&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
private createService(
  id: string,
  cluster: Cluster,
  image: ContainerImage,
  namespace: PrivateDnsNamespace,
  mesh: Mesh,
  envOverwrite: { [key: string]: string } = {},
): {
  service: FargateService,
  port: number,
  privateDnsName: string,
  node: VirtualNode,
} {
// ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now inside the &lt;code&gt;createService&lt;/code&gt; function we can create our VirtualNode and return it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const bgNode = new VirtualNode(this, `${id}-virtual-node`, {
  mesh,
  virtualNodeName: `${id}-virtual-node`,
  accessLog: AccessLog.fromFilePath('/dev/stdout'),
  serviceDiscovery: ServiceDiscovery.cloudMap(service.cloudMapService!),
  listeners: [VirtualNodeListener.http({
    port: appPort,
    healthCheck: HealthCheck.http({
      path: 'health', // no forward slash, this makes them appear in x-ray later on... this took me 3 hours to figure out (╯°□°)╯︵ ┻━┻
    }),
  })],
});

return {
  service,
  port: appPort,
  privateDnsName: `${id}.${namespace.namespaceName}`,
  node: bgNode
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: attach an envoy-sidecar image
&lt;/h3&gt;

&lt;p&gt;in addition we need to add a new containerDefinition for the envoy-proxy. AWS provides the envoi-proxy image under region-specific paths, however, I chose to use the global image.&lt;br&gt;
A thing that is easy to miss: We need to add the managed policy &lt;code&gt;AWSAppMeshEnvoyAccess&lt;/code&gt; to the taskRole&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const virtualNodeName = `${id}-virtual-node`;
const envoyContainer = taskDef.addContainer('envoy', {
  image: ContainerImage.fromRegistry('public.ecr.aws/appmesh/aws-appmesh-envoy:v1.19.1.0-prod'),
  essential: true,
  environment: {
    'APPMESH_RESOURCE_ARN': `arn:aws:appmesh:${Aws.REGION}:${Aws.ACCOUNT_ID}:mesh/${mesh.meshName}/virtualNode/${virtualNodeName}`,
  },
  healthCheck: {
    command: [
      'CMD-SHELL',
      'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE',
    ],
    interval: Duration.seconds(5),
    timeout: Duration.seconds(2),
    startPeriod: Duration.seconds(10),
    retries: 3,
  },
  user: '1337', // important later for the proxy config
  logging: new AwsLogDriver({
    streamPrefix: `${id}/envoy/`,
  }),
});
taskDef.taskRole.addManagedPolicy(
  ManagedPolicy.fromAwsManagedPolicyName('AWSAppMeshEnvoyAccess'),
);
envoyContainer.addUlimits({ name: UlimitName.NOFILE, hardLimit: 15000, softLimit: 15000 });
appContainer.addContainerDependencies({
  container: envoyContainer,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now we have our envoy-sidecar deployed. However, the traffic needs to be routed through the proxy so it can do its job. For that, we will add a &lt;code&gt;proxyConfiguration&lt;/code&gt; to the &lt;code&gt;TaskDef&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;proxyConfiguration: new AppMeshProxyConfiguration({
  containerName: 'envoy',
  properties: {
    ignoredUID: 1337, // user from envoy-container
    appPorts: [ appPort ],
    proxyIngressPort: 15000,
    proxyEgressPort: 15001,
    egressIgnoredIPs: [ '169.254.170.2', '169.254.169.254' ],
  },
}),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: create virtual service
&lt;/h3&gt;

&lt;p&gt;In this step, we will create our virtual service and add some routing. &lt;em&gt;we could not have the router however, it enables you easily modify it to tinker around with routing&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private createVirtualService(
  serviceName: string,
  mesh: Mesh,
  backendNode: VirtualNode,
  namespace: PrivateDnsNamespace,
): VirtualService {
  const router = new VirtualRouter(this, `${serviceName}-virtual-router`, {
    mesh,
    listeners: [VirtualRouterListener.http(3000)],
  });

  router.addRoute('default', {
    routeSpec: RouteSpec.http({
      weightedTargets: [{
        weight: 1,
        virtualNode: backendNode,
      }],
    }),
  });

  const service = new VirtualService(this, serviceName, {
    virtualServiceProvider: VirtualServiceProvider.virtualRouter(router),
    virtualServiceName: `${serviceName}.${namespace.namespaceName}`,
  });
  // https://docs.aws.amazon.com/app-mesh/latest/userguide/troubleshoot-connectivity.html#ts-connectivity-dns-resolution-virtual-service
  new Service(this, `${serviceName}-dummy-service`, {
    namespace,
    name: serviceName,
    dnsRecordType: DnsRecordType.A,
    description: 'The dummy for App Mesh',
  }).registerIpInstance('dummy-instance', { ipv4: '10.10.10.10' });

  return service;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and call the method right after creating the &lt;code&gt;createService&lt;/code&gt; invocation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// [...]
const backendVService = this.createVirtualService('color-service', mesh, backendNode, namespace);
// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now our service will be available (from a meshified node) under the DNS name &lt;code&gt;color-service.service.local&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;next step we also need to update the environment variable in our gateway service so it knows where to find our color-backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COLOR_BACKEND: `http://${backendVService.virtualServiceName}:${backendPort}`,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we need to tell the virtual node of the gateway service that we require to access the service&lt;br&gt;
(you also need to name the node attribute from the creteService function)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// [...]
const { service: clientFGService, port: appPort, node: clientNode } = this.createService(
      'client-1',
// [...]
clientNode.addBackend(Backend.virtualService(backendVService));
// [...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now let's deploy the stack and check our result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The routing will be taken care of by app-mesh. Theoretically, we're done however, the ALB still directly routs the traffic to one of the microservices, so technically it is exposed (and we don't get normalized metrics for it). &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: create an ingress gateway
&lt;/h3&gt;

&lt;p&gt;In this step, we will deploy an envoy-proxy as the ingress gateway. We just need to deploy envoy-proxy as the main container, expose the application port (8080) and configure it as a &lt;code&gt;VirtualGateway&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These are a lot of steps that are just a tutorial of deploying an app as a fargate-container. &lt;br&gt;
Important here is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;taskRole needs managed policy &lt;code&gt;AWSAppMeshEnvoyAccess&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;app port is 8080 however, the health check port is 9901. That means, we need to explicitly allow communication on 9901 between ALB and the APP&lt;/li&gt;
&lt;li&gt;health url is /server_info (for ALB)
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private createIngressService(
  cluster: Cluster,
  listener: ApplicationListener,
  mesh: Mesh,
): {
  gateway: VirtualGateway,
  fgService: FargateService,
} {
  const port = 8080;
  const gateway = new VirtualGateway(this, 'virtual-gateway', {
    mesh,
    listeners: [VirtualGatewayListener.http({ port })],
  });

  const taskDef = new FargateTaskDefinition(this, 'ingress-fargate-task-def', {
    memoryLimitMiB: 512,
  });

  const container = taskDef.addContainer('ingress-app', {
    // most up-to-date envoy image at the point of writing the article
    image: ContainerImage.fromRegistry('public.ecr.aws/appmesh/aws-appmesh-envoy:v1.19.1.0-prod'),
    logging: new AwsLogDriver({ streamPrefix: 'ingress-app-' }),
    portMappings: [
      { containerPort: port },
      { containerPort: 9901 }, // for health check
    ],
    healthCheck: {
      // health check from Documentation
      command: ['CMD-SHELL', 'curl -s http://localhost:9901/server_info | grep state | grep -q LIVE || exit 1'],
      interval: Duration.seconds(5),
      timeout: Duration.seconds(2),
      startPeriod: Duration.seconds(10),
      retries: 3,
    },
    environment: {
      APPMESH_VIRTUAL_NODE_NAME: `mesh/${mesh.meshName}/virtualGateway/${gateway.virtualGatewayName}`, // important, otherwhise envoiy can't fetch the config
      AWS_REGION: Aws.REGION,
    },
    memoryLimitMiB: 320, // limit examples from the official docs
    cpu: 208, // limit examples from the official docs
  });
  // limit examples from the official docs
  container.addUlimits({
    name: UlimitName.NOFILE,
    hardLimit: 1024000,
    softLimit: 1024000,
  });
  const service = new FargateService(this, 'ingress-service', {
    cluster,
    assignPublicIp: true, // for public vpc
    minHealthyPercent: 0, // for zero downtime rolling deployment set desiredcount=2 and minHealty = 50
    desiredCount: 1,
    taskDefinition: taskDef,
    vpcSubnets: { subnetType: SubnetType.PUBLIC },
    securityGroups: [new SecurityGroup(this, 'ingress-default-sg', {
      securityGroupName: 'ingress-fargate-service',
      vpc: cluster.vpc,
    })],
  });
  listener.addTargets('ingress-gateway-target', {
    port,
    protocol: ApplicationProtocol.HTTP,
    healthCheck: {
      protocol: ELBProtocol.HTTP,
      interval: Duration.seconds(10),
      // health port from aws-envoy docs
      port: '9901',
      // health check path from aws-envoy docs
      path: '/server_info',
    },
    targets: [service],
    deregistrationDelay: Duration.seconds(0), // not needed, just speeds up the deployment for this example
  });

  // required so the ALB can reach the health-check endpoint
  service.connections.allowFrom(listener, Port.tcp(9901));
  // required so the service can fetch the documentation
  taskDef.taskRole.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('AWSAppMeshEnvoyAccess'));

  return {gateway, fgService: service};
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Lastly, we just need to remove the ALB ListenerTarget pointing to our clientService. The call of the function &lt;code&gt;createIngressService&lt;/code&gt; will attach a ListenerTarget pointing to our gateway. &lt;/p&gt;

&lt;p&gt;for this we quickly create a virtual service for our clientService as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const clientVService = this.createVirtualService('client-service', mesh, clientNode, namespace);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;create the ingress service with our previously created method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {gateway: virtualGateway, fgService: virtualGatewayFGService} = this.createIngressService(cluster, listener, mesh);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and point the VirtualGateway to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const clientVRouter = new VirtualRouter(this, 'client-virtual-router', {
  mesh,
  listeners: [VirtualRouterListener.http(3000)],
});

clientVRouter.addRoute('default', {
  routeSpec: RouteSpec.http({
    weightedTargets: [{
      weight: 1,
      virtualNode: clientNode,
    }],
  }),
});

virtualGateway.addGatewayRoute('web-route', {
  routeSpec: GatewayRouteSpec.http({
    routeTarget: clientVService,
    match: {
      path: HttpGatewayRoutePathMatch.startsWith('/'),
    },
  }),
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bonus - AWS-X-Ray:
&lt;/h3&gt;

&lt;p&gt;using x-ray is very easy, we only need to deploy an x-ray sidecar image and enable envoy x-ray:&lt;/p&gt;

&lt;p&gt;First, let's modify both &lt;code&gt;createService&lt;/code&gt; and &lt;code&gt;createIngressService&lt;/code&gt; to deploy the x-ray sidecar image and grant XRay policies to the taskRole&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;taskDef.addContainer(`${id}-xray`, {
  image: ContainerImage.fromRegistry('public.ecr.aws/xray/aws-xray-daemon:latest'),
  essential: false,
  memoryReservationMiB: 256,
  environment: {
    AWS_REGION: Aws.REGION,
  },
  user: '1337', // X-Ray traffic should not go through Envoy proxy
  logging: new AwsLogDriver({
    streamPrefix: id + '-xray-',
  }),
  portMappings: [ {
    containerPort: 2000,
    protocol: Protocol.UDP,
  } ],
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;taskDef.addContainer(`ingress-xray`, {
  image: ContainerImage.fromRegistry('public.ecr.aws/xray/aws-xray-daemon:latest'),
  essential: false,
  memoryReservationMiB: 256,
  environment: {
    AWS_REGION: Aws.REGION,
  },
  user: '1337', // X-Ray traffic should not go through Envoy proxy
  logging: new AwsLogDriver({
    streamPrefix: 'ingress-xray-',
  }),
  portMappings: [ {
    containerPort: 2000,
    protocol: Protocol.UDP,
  } ],
});
taskDef.taskRole.addManagedPolicy(
  ManagedPolicy.fromAwsManagedPolicyName('AWSXRayDaemonWriteAccess'),
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(note: only that the id property got replaced by a constant)&lt;/p&gt;

&lt;p&gt;now we need to enable x-ray on all the envoy containers by adding the env var &lt;code&gt;'ENABLE_ENVOY_XRAY_TRACING': '1'&lt;/code&gt; to the envoy-sidecar image (right underneath &lt;code&gt;APPMESH_RESOURCE_ARN&lt;/code&gt;) &lt;/p&gt;

&lt;p&gt;Finally, let's run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx cdk deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the website and wait a minute. &lt;br&gt;
Now let's go to the &lt;a href="https://eu-west-1.console.aws.amazon.com/xray/home" rel="noopener noreferrer"&gt;x-ray console&lt;/a&gt; and check the results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpues3ztoa50bu4682xjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpues3ztoa50bu4682xjh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Now that you have your mesh configured: What can you do with it?&lt;/p&gt;

&lt;h3&gt;
  
  
  First:
&lt;/h3&gt;

&lt;p&gt;You could deploy multiple instances (virtual nodes / fg services) and play around with the routing. &lt;br&gt;
For example, you can route by cookie or play around with the weights and see what happens. &lt;/p&gt;

&lt;p&gt;second: I intentionally put everything into one file, so it's easy to browse and to understand, but it screams for a refactoring :) &lt;/p&gt;

&lt;p&gt;third: &lt;br&gt;
if you want to access a 3rd party API you can also add monitoring for that by providing a virtual service. I haven't tested this, however &lt;a href="https://aws.amazon.com/blogs/containers/service-connectivity-inside-and-outside-the-mesh-using-aws-app-mesh-ecs-fargate/" rel="noopener noreferrer"&gt;the docs mention it&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>iac</category>
      <category>cdk</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
