constructor()

in source/4-containerized-service/cdk/lib/infrastructure-stack.ts [30:182]


  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    Tags.of(this).add('ServiceName', this.node.tryGetContext('service_name'));

    const vpc = new Vpc(this, 'MyVpc', { maxAzs: 2 });

    ////////// CLOUDFRONT //////////////

    const cloudFront = new CloudFrontToS3(this, 'my-cloudfront-s3', {});

    // prepopulate bucket with a few images
    new BucketDeployment(this, 'DeployS3Images', {
      sources: [Source.asset('./static')],
      destinationBucket: cloudFront.s3Bucket!,
      destinationKeyPrefix: 'static'
    });
    const staticDomain =
      cloudFront.cloudFrontWebDistribution.distributionDomainName + '/static';

    ////////// Database ////////////

    const db = new ServerlessCluster(this, 'MyDatabase', {
      engine: DatabaseClusterEngine.AURORA_MYSQL,
      defaultDatabaseName: 'ecommerce',
      enableHttpEndpoint: true,
      vpc
    });

    // prepopulate the Database with a few products
    const createTable = new AwsCustomResource(this, 'CreateTable', {
      onCreate: {
        service: 'RDSDataService',
        action: 'executeStatement',
        parameters: {
          resourceArn: db.clusterArn,
          secretArn: db.secret?.secretArn,
          database: 'ecommerce',
          sql:
            'CREATE TABLE products ( productId int, name varchar(255), image varchar(255), price decimal(5, 2) );'
        },
        physicalResourceId: PhysicalResourceId.of(Date.now().toString())
      },
      policy: AwsCustomResourcePolicy.fromSdkCalls({
        resources: AwsCustomResourcePolicy.ANY_RESOURCE
      })
    });
    db.secret?.grantRead(createTable);

    const insertTable = new AwsCustomResource(this, 'InsertTable', {
      onCreate: {
        service: 'RDSDataService',
        action: 'executeStatement',
        parameters: {
          resourceArn: db.clusterArn,
          secretArn: db.secret?.secretArn,
          database: 'ecommerce',
          sql: `INSERT INTO products VALUES ( 1, 'hat', 'https://${staticDomain}/hat.jpeg', 12.55), ( 2, 'shoe', 'https://${staticDomain}/shoe.jpg', 19.85);`
        },
        physicalResourceId: PhysicalResourceId.of(Date.now().toString())
      },
      policy: AwsCustomResourcePolicy.fromSdkCalls({
        resources: AwsCustomResourcePolicy.ANY_RESOURCE
      })
    });
    db.secret?.grantRead(insertTable);

    insertTable.node.addDependency(createTable);

    ////////// ECS ////////////

    const cluster = new Cluster(this, 'MyCluster', { vpc });

    const NGINXLogDriver = new AwsLogDriver({ streamPrefix: 'myNGINX' });
    const PHPLogDriver = new AwsLogDriver({ streamPrefix: 'myPHP' });

    const taskDefinition = new FargateTaskDefinition(this, 'TaskDef');

    const nginxContainer = taskDefinition.addContainer('ab-nginx', {
      image: ContainerImage.fromAsset(__dirname + '/../../nginx'),
      environment: {
        PHP_HOST: 'localhost'
      },
      logging: NGINXLogDriver
    });
    nginxContainer.addPortMappings({ containerPort: 80 });

    const fargateService = new ApplicationLoadBalancedFargateService(
      this,
      'MyFargateService',
      {
        cluster: cluster,
        desiredCount: 2,
        taskDefinition,
        publicLoadBalancer: true
      }
    );
    this.loadBalancer = fargateService.loadBalancer;

    const phpContainer = taskDefinition.addContainer('ab-php', {
      image: ContainerImage.fromAsset(__dirname + '/../../php-fpm'),
      environment: {
        DOMAIN: 'http://' + fargateService.loadBalancer.loadBalancerDnsName
      },
      secrets: {
        SECRETS: Secret.fromSecretsManager(db.secret!)
      },
      logging: PHPLogDriver
    });
    phpContainer.addPortMappings({ containerPort: 9000 });

    db.connections.allowDefaultPortFromAnyIpv4();

    /////////////////// CloudWatch DashBoard /////////////////////

    const dashboard = new Dashboard(this, 'MyDashboard');
    dashboard.addWidgets(
      new TextWidget({
        markdown:
          '# Load Balancer\nmetrics to monitor load balancer metrics:\n* Amount of incoming requests\n* Latency with an alarm if max accepted latency exceeded.',
        width: 6,
        height: 6
      }),
      new GraphWidget({
        title: 'Requests',
        width: 9,
        left: [fargateService.loadBalancer.metricRequestCount()]
      }),
      new GraphWidget({
        title: 'Latency',
        width: 9,
        left: [fargateService.loadBalancer.metricTargetResponseTime()]
      })
    );
    dashboard.addWidgets(
      new LogQueryWidget({
        title: 'NGINX Logs',
        width: 24,
        logGroupNames: [NGINXLogDriver.logGroup?.logGroupName!],
        view: LogQueryVisualizationType.TABLE,
        queryLines: ['fields @timestamp, @message']
      })
    );
    dashboard.addWidgets(
      new LogQueryWidget({
        title: 'PHP Logs',
        width: 24,
        logGroupNames: [PHPLogDriver.logGroup?.logGroupName!],
        view: LogQueryVisualizationType.TABLE,
        queryLines: ['fields @timestamp, @message']
      })
    );
  }