简体   繁体   中英

How to run Fargate task after database instance is created with AWS CDK?

I have a AWS CDK stack that creates a Fargate task (with ApplicationLoadBalancedFargateService ) from a docker container. The container is a web app that connects to database. When I deploy the CDK stack, it takes seven minutes to create a database instance. However, the Fargate task is launched quicker, so the task is stopped because it can not connect to database (because is has not been created yet). Then it tries to start the task again, fails and stops. This repeats four times, until the database is created.

Question: is there a way to delay the staring of the Fargate task until the database is creation is finished in the CDK code?

Here is the CDK code, I'm using version 2.30.0 of the aws-cdk library.

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns';
import * as secretManager from "aws-cdk-lib/aws-secretsmanager";
import { Credentials, DatabaseInstance, DatabaseInstanceEngine, DatabaseSecret, PostgresEngineVersion } from 'aws-cdk-lib/aws-rds';
import { SecurityGroup } from 'aws-cdk-lib/aws-ec2';

export class RailsEcsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const stackPrefix = 'PgRails'

    const vpc = new ec2.Vpc(this, `${stackPrefix}VPC`, {
      maxAzs: 3,
      subnetConfiguration: [
        {
          name: 'Public',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 28,
        },
        {
          name: 'Private',
          subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
          cidrMask: 24,
        },
        {
          name: 'Isolated',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 28,
        },
      ],
    });


    // Security groups
    // ------------

    // Fargate security group
    const fargateSecurityGroup = new SecurityGroup(this, `${stackPrefix}FargateSecurityGroup`, { vpc })

    // Database security group
    const databaseSecurityGroup = new SecurityGroup(this, `${stackPrefix}DatabaseSecurityGroup`, {
      vpc,
      allowAllOutbound: false
    });

    databaseSecurityGroup.addIngressRule(
      fargateSecurityGroup,
      ec2.Port.tcp(5432),
      'Permit the database to accept requests from the fargate service'
    );


    // Database
    // -----------

    const databaseSecret = new DatabaseSecret(this, `${stackPrefix}DatabaseSecret`, {
      username: 'postgres',
    });

    new DatabaseInstance(this, `${stackPrefix}DatabaseInstance`, {
      vpc,
      securityGroups: [databaseSecurityGroup],
      engine: DatabaseInstanceEngine.postgres({
        version: PostgresEngineVersion.VER_14_2,
      }),
      removalPolicy: cdk.RemovalPolicy.DESTROY, // CHANGE TO .SNAPSHOT FOR PRODUCTION !!!!!!!!!!!!!!!!!!!!!!
      credentials: Credentials.fromSecret(databaseSecret),
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MICRO),
      vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
      },
      backupRetention: cdk.Duration.days(7),
      allocatedStorage: 10,
      maxAllocatedStorage: 30,
      databaseName: 'rails_postgres_production',
    });


    // Fargate service
    // ------------

    const repo = ecr.Repository.fromRepositoryName(this, 'pg_rails_docker', 'pg_rails_docker');
    const image =  ecs.ContainerImage.fromEcrRepository(repo, 'latest');
    const cluster = new ecs.Cluster(this, "DataCluster", { vpc });
    const mySecret = secretManager.Secret.fromSecretNameV2(this, 'MySecret', 'MySecret');

    const webLogging = new ecs.AwsLogDriver({
      streamPrefix: "my_pg_rails",
    })

    const webTaskDefinition = new ecs.FargateTaskDefinition(this, `${stackPrefix}FargateTask`, {
      cpu: 256,
      memoryLimitMiB: 512,
      runtimePlatform: {
        operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
        cpuArchitecture: ecs.CpuArchitecture.ARM64
      },
    });

    webTaskDefinition.addContainer(`${stackPrefix}Container`, {
      image: image,
      portMappings: [{ containerPort: 80 }],
      secrets: {
        RDS_HOSTNAME: ecs.Secret.fromSecretsManager(databaseSecret, 'host'),
        RDS_PORT: ecs.Secret.fromSecretsManager(databaseSecret, 'port'),
        RDS_USERNAME: ecs.Secret.fromSecretsManager(databaseSecret, 'username'),
        RDS_PASSWORD: ecs.Secret.fromSecretsManager(databaseSecret, 'password'),
        TRIST: ecs.Secret.fromSecretsManager(mySecret, 'TRIST')
      },
      logging: webLogging,
    });

    new ecsp.ApplicationLoadBalancedFargateService(this, `${stackPrefix}WebServer`, {
      cluster: cluster,
      taskDefinition: webTaskDefinition,
      securityGroups: [fargateSecurityGroup],
      publicLoadBalancer: true
    });
  }
}

if you want to database run before fargate, you should do something to make fargate depends on database. In my case, I just do below for a trick.

First, declare database as const

const database = new DatabaseInstance(this, `${stackPrefix}DatabaseInstance`, {
...

Then, call database variable somewhere when define fargate service.

example:

webTaskDefinition.addContainer(`${stackPrefix}Container`, {
      image: image,
      portMappings: [{ containerPort: 80 }],
      secrets: {
        ...
        TRICK: database.endpoint // or something like this, Idk exactly grammar

      },
      logging: webLogging,
    });

Hope it can help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM