问:
Remove unnecessary AWS resources, VPC + NAT gateway
I recently set up an application on AWS via CDK. The application consists of a Dockerized nodejs application, which connects to an RDS instance, and has a Redis caching layer as well. After having the application deployed for a few days, the costs are much higher than I had anticipated, even with minimal traffic. After looking through the cost explorer, it looks like half of the cost is coming from the NAT gateways.

In my current setup, I have created two VPCs. One is used for the application stack, and the other is for the CodePipeline. I needed to add one for the pipeline because without it I was hitting rate limits when trying to pull Docker images during the CodeBuildAction steps.

I'm not very comfortable with the networking bits, but I feel like there are extra resources involved. The pipeline VPC has three NAT gateways and three EIPs. These end up just sitting there waiting for the next deployment, which seems like a huge waste. It seems like a new gateway + EIP is allocated for each construct the VPC is attached to in CDK. Can I just make it reuse the same one? Is there an alternative to adding a VPC at all and not getting rate limited by Docker?

I also find it very surprising (I might just be naive) that the NAT gateway is so far equally as expensive as my current Fargate task costs. Is there an alternative that would serve my purposes, but come at a little lower cost?

Anyways, here are my two stacks:

// pipeline-stack.ts

import { SecretValue, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { Artifact, IStage, Pipeline } from "aws-cdk-lib/aws-codepipeline";
import {
  CloudFormationCreateUpdateStackAction,
  CodeBuildAction,
  CodeBuildActionType,
  GitHubSourceAction,
} from "aws-cdk-lib/aws-codepipeline-actions";
import {
  BuildEnvironmentVariableType,
  BuildSpec,
  LinuxBuildImage,
  PipelineProject,
} from "aws-cdk-lib/aws-codebuild";
import { SnsTopic } from "aws-cdk-lib/aws-events-targets";
import { Topic } from "aws-cdk-lib/aws-sns";
import { EventField, RuleTargetInput } from "aws-cdk-lib/aws-events";
import { EmailSubscription, SmsSubscription } from "aws-cdk-lib/aws-sns-subscriptions";
import ApiStack from "./stacks/api-stack";
import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
import { Secret } from "aws-cdk-lib/aws-ecs";
import { BuildEnvironmentVariable } from "aws-cdk-lib/aws-codebuild/lib/project";
import * as SecretsManager from "aws-cdk-lib/aws-secretsmanager";
import { getApplicationEnvironment, getApplicationSecrets } from "./secrets-helper";

const capFirst = (str: string): string => {
  return str.charAt(0).toUpperCase() + str.slice(1);
};

interface PipelineStackProps extends StackProps {
  environment: string;
  emailAddress: string;
  phoneNumber: string;
  branch: string;
  secrets: {
    arn: string;
  };
  repo: {
    uri: string;
    name: string;
  };
}

export class PipelineStack extends Stack {
  private readonly envName: string;
  private readonly pipeline: Pipeline;

  // source outputs
  private cdkSourceOutput: Artifact;
  private applicationSourceOutput: Artifact;

  // code source actions
  private cdkSourceAction: GitHubSourceAction;
  private applicationSourceAction: GitHubSourceAction;

  // build outputs
  private cdkBuildOutput: Artifact;
  private applicationBuildOutput: Artifact;

  // notifications
  private pipelineNotificationsTopic: Topic;

  private readonly codeBuildVpc: Vpc;
  private readonly codeBuildSecurityGroup: SecurityGroup;

  private readonly secrets: SecretsManager.ISecret;
  private readonly ecrCodeBuildRole: Role;

  // stages
  private sourceStage: IStage;
  private selfMutateStage: IStage;
  private buildStage: IStage;
  private apiTestsStage: IStage;

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

    this.envName = props.environment;
    this.addNotifications(props);

    this.ecrCodeBuildRole = new Role(this, "application-build-project-role", {
      assumedBy: new ServicePrincipal("codebuild.amazonaws.com"),
      managedPolicies: [
        ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryPowerUser"),
      ],
    });

    this.codeBuildVpc = new Vpc(this, "codebuild-vpc", {
      vpcName: "codebuild-vpc",
      enableDnsSupport: true,
    });
    this.codeBuildSecurityGroup = new SecurityGroup(this, "codebuild-vpc-security-group", {
      vpc: this.codeBuildVpc,
      allowAllOutbound: true,
    });

    this.secrets = SecretsManager.Secret.fromSecretCompleteArn(this, "secrets", props.secrets.arn);

    this.pipeline = new Pipeline(this, "pipeline", {
      pipelineName: `${capFirst(this.envName)}Pipeline`,
      crossAccountKeys: false,
      restartExecutionOnUpdate: true,
    });

    // STAGE 1 - Source Stage
    this.addSourceStage(props);

    // STAGE 2 - Build Stage
    this.addBuildStage(props);

    // STAGE 3: SelfMutate Stage
    this.addSelfMutateStage();

    // STAGE 4: Testing
    this.addTestStage();
  }

  addNotifications(props: PipelineStackProps) {
    this.pipelineNotificationsTopic = new Topic(this, "pipeline-notifications-topic", {
      topicName: `PipelineNotifications${capFirst(props.environment)}`,
    });
    this.pipelineNotificationsTopic.addSubscription(new EmailSubscription(props.emailAddress));
    this.pipelineNotificationsTopic.addSubscription(new SmsSubscription(props.phoneNumber));
  }

  /**
   * Stage 1
   */
  addSourceStage(props: PipelineStackProps) {
    this.cdkSourceOutput = new Artifact("cdk-source-output");
    this.cdkSourceAction = new GitHubSourceAction({
      actionName: "CdkSource",
      owner: "my-org",
      repo: "my-cdk-repo",
      branch: "main",
      oauthToken: SecretValue.secretsManager("/connections/github/access-token"),
      output: this.cdkSourceOutput,
    });

    this.applicationSourceOutput = new Artifact("ApplicationSourceOutput");
    this.applicationSourceAction = new GitHubSourceAction({
      actionName: "ApplicationSource",
      owner: "my-org",
      repo: "my-application-repo",
      branch: props.branch,
      oauthToken: SecretValue.secretsManager("/connections/github/access-token"),
      output: this.applicationSourceOutput,
    });

    this.sourceStage = this.pipeline.addStage({
      stageName: "Source",
      actions: [this.cdkSourceAction, this.applicationSourceAction],
    });
  }

  /**
   * stage 2
   */
  addBuildStage(props: PipelineStackProps) {
    const cdkBuildAction = this.createCdkBuildAction();
    const applicationBuildAction = this.createApplicationBuildAction(props);
    this.buildStage = this.pipeline.addStage({
      stageName: "Build",
      actions: [cdkBuildAction, applicationBuildAction],
    });
  }

  /**
   * stage 3
   */
  addSelfMutateStage() {
    this.selfMutateStage = this.pipeline.addStage({
      stageName: "PipelineUpdate",
      actions: [
        new CloudFormationCreateUpdateStackAction({
          actionName: "PipelineCreateUpdateStackAction",
          stackName: this.stackName,
          templatePath: this.cdkBuildOutput.atPath(`${this.stackName}.template.json`),
          adminPermissions: true,
        }),
      ],
    });
  }

  /**
   * stage 4
   */
  addTestStage() {
    const testAction = new CodeBuildAction({
      actionName: "RunApiTests",
      type: CodeBuildActionType.TEST,
      input: this.applicationSourceOutput,
      project: new PipelineProject(this, "api-tests-project", {
        vpc: this.codeBuildVpc,
        securityGroups: [this.codeBuildSecurityGroup],
        environment: {
          buildImage: LinuxBuildImage.STANDARD_5_0,
          privileged: true,
        },
        buildSpec: BuildSpec.fromObject({
          version: "0.2",
          phases: {
            install: {
              commands: ["cp .env.testing .env"],
            },
            build: {
              commands: [
                "ls",
                "docker-compose -f docker-compose.staging.yml run -e NODE_ENV=testing --rm api node ace test",
              ],
            },
          },
        }),
      }),
      runOrder: 1,
    });
    this.apiTestsStage = this.pipeline.addStage({
      stageName: "RunApiTests",
      actions: [testAction],
    });
  }

  createCdkBuildAction() {
    this.cdkBuildOutput = new Artifact("CdkBuildOutput");
    return new CodeBuildAction({
      actionName: "CdkBuildAction",
      input: this.cdkSourceOutput,
      outputs: [this.cdkBuildOutput],
      project: new PipelineProject(this, "cdk-build-project", {
        environment: {
          buildImage: LinuxBuildImage.STANDARD_5_0,
        },
        buildSpec: BuildSpec.fromSourceFilename("build-specs/cdk-build-spec.yml"),
      }),
    });
  }

  createApplicationBuildAction(props: PipelineStackProps) {
    this.applicationBuildOutput = new Artifact("ApplicationBuildOutput");

    const project = new PipelineProject(this, "application-build-project", {
      vpc: this.codeBuildVpc,
      securityGroups: [this.codeBuildSecurityGroup],
      environment: {
        buildImage: LinuxBuildImage.STANDARD_5_0,
        privileged: true,
      },
      environmentVariables: {
        ENV: {
          value: this.envName,
        },
        ECR_REPO_URI: {
          value: props.repo.uri,
        },
        ECR_REPO_NAME: {
          value: props.repo.name,
        },
        AWS_REGION: {
          value: props.env!.region,
        },
      },
      buildSpec: BuildSpec.fromObject({
        version: "0.2",
        phases: {
          pre_build: {
            commands: [
              "echo 'Logging into Amazon ECR...'",
              "aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO_URI",
              'COMMIT_HASH=$(echo "$CODEBUILD_RESOLVED_SOURCE_VERSION" | head -c 8)',
            ],
          },
          build: {
            commands: ["docker build -t $ECR_REPO_NAME:latest ."],
          },
          post_build: {
            commands: [
              "docker tag $ECR_REPO_NAME:latest $ECR_REPO_URI/$ECR_REPO_NAME:latest",
              "docker tag $ECR_REPO_NAME:latest $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH",
              "docker push $ECR_REPO_URI/$ECR_REPO_NAME:latest",
              "docker push $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH",
            ],
          },
        },
      }),
      role: this.ecrCodeBuildRole,
    });

    return new CodeBuildAction({
      actionName: "ApplicationBuildAction",
      input: this.applicationSourceOutput,
      outputs: [this.applicationBuildOutput],
      project: project,
    });
  }

  public addDatabaseMigrationStage(apiStack: ApiStack, stageName: string): IStage {
    let buildEnv: { [name: string]: BuildEnvironmentVariable } = {
      ENV: {
        value: this.envName,
      },
      ECR_REPO_URI: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: apiStack.repoUri,
      },
      ECR_REPO_NAME: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: apiStack.repoName,
      },
      AWS_REGION: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: this.region,
      },
    };
    buildEnv = this.getBuildEnvAppSecrets(getApplicationSecrets(this.secrets), buildEnv);
    buildEnv = this.getBuildEnvAppEnvVars(
      getApplicationEnvironment({
        REDIS_HOST: apiStack.redisHost.importValue,
        REDIS_PORT: apiStack.redisPort.importValue,
      }),
      buildEnv,
    );

    let envVarNames = Object.keys(buildEnv);

    const envFileCommand = `printenv | grep '${envVarNames.join("\\|")}' >> .env`;

    return this.pipeline.addStage({
      stageName: stageName,
      actions: [
        new CodeBuildAction({
          actionName: "DatabaseMigrations",
          input: this.applicationSourceOutput,
          project: new PipelineProject(this, "database-migrations-project", {
            description: "Run database migrations against RDS database",
            environment: {
              buildImage: LinuxBuildImage.STANDARD_5_0,
              privileged: true,
            },
            environmentVariables: buildEnv,
            buildSpec: BuildSpec.fromObject({
              version: "0.2",
              phases: {
                pre_build: {
                  commands: [
                    "echo 'Logging into Amazon ECR...'",
                    "aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO_URI",
                    'COMMIT_HASH=$(echo "$CODEBUILD_RESOLVED_SOURCE_VERSION" | head -c 8)',
                    envFileCommand,
                    "cat .env",
                  ],
                },
                build: {
                  commands: [
                    `docker run --env-file .env --name api $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH node ace migration:run --force`,
                    ": > .env",
                  ],
                },
              },
            }),
            role: this.ecrCodeBuildRole,
          }),
        }),
      ],
    });
  }

  private getBuildEnvAppSecrets(
    secrets: { [key: string]: Secret },
    buildEnv: { [name: string]: BuildEnvironmentVariable },
  ): { [name: string]: BuildEnvironmentVariable } {
    for (let key in secrets) {
      buildEnv[key] = {
        type: BuildEnvironmentVariableType.SECRETS_MANAGER,
        value: `${this.secrets.secretArn}:${key}`,
      };
    }

    return buildEnv;
  }

  private getBuildEnvAppEnvVars(
    vars: { [key: string]: string },
    buildEnv: { [name: string]: BuildEnvironmentVariable },
  ): { [name: string]: BuildEnvironmentVariable } {
    for (let key in vars) {
      buildEnv[key] = {
        value: vars[key],
      };
    }

    return buildEnv;
  }

  public addApplicationStage(apiStack: ApiStack, stageName: string): IStage {
    return this.pipeline.addStage({
      stageName: stageName,
      actions: [
        new CloudFormationCreateUpdateStackAction({
          actionName: "ApplicationUpdate",
          stackName: apiStack.stackName,
          templatePath: this.cdkBuildOutput.atPath(`${apiStack.stackName}.template.json`),
          adminPermissions: true,
        }),
      ],
    });
  }
}

```
<pre class="lang-js prettyprint-override"><code>// api-stack.ts

import { CfnOutput, CfnResource, Lazy, Stack, StackProps } from "aws-cdk-lib";
import * as EC2 from "aws-cdk-lib/aws-ec2";
import { ISubnet } from "aws-cdk-lib/aws-ec2";
import * as ECS from "aws-cdk-lib/aws-ecs";
import { DeploymentControllerType, ScalableTaskCount } from "aws-cdk-lib/aws-ecs";
import * as EcsPatterns from "aws-cdk-lib/aws-ecs-patterns";
import * as RDS from "aws-cdk-lib/aws-rds";
import { Credentials } from "aws-cdk-lib/aws-rds";
import * as Route53 from "aws-cdk-lib/aws-route53";
import * as Route53Targets from "aws-cdk-lib/aws-route53-targets";
import * as ECR from "aws-cdk-lib/aws-ecr";
import * as CertificateManager from "aws-cdk-lib/aws-certificatemanager";
import * as SecretsManager from "aws-cdk-lib/aws-secretsmanager";
import * as ElasticCache from "aws-cdk-lib/aws-elasticache";
import { Construct } from "constructs";
import { getApplicationEnvironment, getApplicationSecrets } from "../secrets-helper";

export type ApiStackProps = StackProps & {
  environment: string;
  hostedZone: {
    id: string;
    name: string;
  };
  domainName: string;
  scaling: {
    desiredCount: number;
    maxCount: number;
    cpuPercentage: number;
    memoryPercentage: number;
  };
  repository: {
    uri: string;
    arn: string;
    name: string;
  };
  secrets: { arn: string };
};

export default class ApiStack extends Stack {
  vpc: EC2.Vpc;
  cluster: ECS.Cluster;
  ecsService: EcsPatterns.ApplicationLoadBalancedFargateService;
  certificate: CertificateManager.ICertificate;
  repository: ECR.IRepository;
  database: RDS.IDatabaseInstance;
  databaseCredentials: Credentials;
  hostedZone: Route53.IHostedZone;
  aliasRecord: Route53.ARecord;
  redis: ElasticCache.CfnReplicationGroup;

  repoUri: string;
  repoName: string;

  applicationEnvVariables: {
    [key: string]: string;
  };

  redisHost: CfnOutput;
  redisPort: CfnOutput;
  gatewayUrl: CfnOutput;

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

    this.repoUri = props.repository.uri;
    this.repoName = props.repository.name;

    this.setUpVpc(props);
    this.setUpRedisCluster(props);
    this.setUpDatabase(props);
    this.setUpCluster(props);
    this.setUpHostedZone(props);
    this.setUpCertificate(props);
    this.setUpRepository(props);
    this.setUpEcsService(props);
    this.setUpAliasRecord(props);
  }

  private resourceName(props: ApiStackProps, resourceType: string): string {
    return `twibs-api-${resourceType}-${props.environment}`;
  }

  private setUpVpc(props: ApiStackProps) {
    this.vpc = new EC2.Vpc(this, this.resourceName(props, "vpc"), {
      maxAzs: 3, // Default is all AZs in region
    });
  }

  private setUpRedisCluster(props: ApiStackProps) {
    const subnetGroup = new ElasticCache.CfnSubnetGroup(this, "cache-subnet-group", {
      cacheSubnetGroupName: "redis-cache-subnet-group",
      subnetIds: this.vpc.privateSubnets.map((subnet: ISubnet) => subnet.subnetId),
      description: "Subnet group for Redis Cache cluster",
    });

    const securityGroup = new EC2.SecurityGroup(this, "redis-security-group", {
      vpc: this.vpc,
      description: `SecurityGroup associated with RedisDB Cluster - ${props.environment}`,
      allowAllOutbound: false,
    });

    securityGroup.addIngressRule(
      EC2.Peer.ipv4(this.vpc.vpcCidrBlock),
      EC2.Port.tcp(6379),
      "Allow from VPC on port 6379",
    );

    this.redis = new ElasticCache.CfnReplicationGroup(this, "redis", {
      numNodeGroups: 1,
      cacheNodeType: "cache.t2.small",
      engine: "redis",
      multiAzEnabled: false,
      autoMinorVersionUpgrade: false,
      cacheParameterGroupName: "default.redis6.x.cluster.on",
      engineVersion: "6.x",
      cacheSubnetGroupName: subnetGroup.ref,
      securityGroupIds: [securityGroup.securityGroupId],
      replicationGroupDescription: "RedisDB setup by CDK",
      replicasPerNodeGroup: 0,
      port: 6379,
    });
  }

  private setUpDatabase(props: ApiStackProps) {
    if (["production", "staging", "develop"].includes(props.environment)) {
      return;
    }

    this.databaseCredentials = Credentials.fromUsername("my_db_username");
    this.database = new RDS.DatabaseInstance(this, "database", {
      vpc: this.vpc,
      engine: RDS.DatabaseInstanceEngine.postgres({
        version: RDS.PostgresEngineVersion.VER_13_4,
      }),
      credentials: this.databaseCredentials,
      databaseName: `my_app_${props.environment}`,
      deletionProtection: true,
    });
  }

  private setUpCluster(props: ApiStackProps) {
    this.cluster = new ECS.Cluster(this, this.resourceName(props, "cluster"), {
      vpc: this.vpc,
      capacity: {
        instanceType: EC2.InstanceType.of(EC2.InstanceClass.T3, EC2.InstanceSize.`SMALL`),
      },
    });
  }

  private setUpHostedZone(props: ApiStackProps) {
    this.hostedZone = Route53.HostedZone.fromHostedZoneAttributes(
      this,
      this.resourceName(props, "hosted-zone"),
      {
        hostedZoneId: props.hostedZone.id,
        zoneName: props.hostedZone.name,
      },
    );
  }

  private setUpCertificate(props: ApiStackProps) {
    this.certificate = new CertificateManager.Certificate(this, "certificate", {
      domainName: props.domainName,
      validation: CertificateManager.CertificateValidation.fromDns(this.hostedZone),
    });
  }

  private setUpRepository(props: ApiStackProps) {
    this.repository = ECR.Repository.fromRepositoryAttributes(
      this,
      this.resourceName(props, "repository"),
      {
        repositoryArn: props.repository.arn,
        repositoryName: props.repository.name,
      },
    );
  }

  private setUpEcsService(props: ApiStackProps) {
    const secrets = SecretsManager.Secret.fromSecretCompleteArn(this, "secrets", props.secrets.arn);
    this.redisHost = new CfnOutput(this, "redis-host-output", {
      value: this.redis.attrConfigurationEndPointAddress,
      exportName: "redis-host-output",
    });
    this.redisPort = new CfnOutput(this, "redis-port-output", {
      value: this.redis.attrConfigurationEndPointPort,
      exportName: "redis-port-output",
    });

    // Create a load-balanced ecs-service service and make it public
    this.ecsService = new EcsPatterns.ApplicationLoadBalancedFargateService(
      this,
      this.resourceName(props, "ecs-service"),
      {
        serviceName: `${props.environment}-api-service`,
        cluster: this.cluster, // Required
        cpu: 256, // Default is 256
        desiredCount: props.scaling.desiredCount, // Default is 1
        taskImageOptions: {
          image: ECS.ContainerImage.fromEcrRepository(this.repository),
          environment: getApplicationEnvironment({
            REDIS_HOST: this.redis.attrConfigurationEndPointAddress,
            REDIS_PORT: this.redis.attrConfigurationEndPointPort,
          }),
          secrets: getApplicationSecrets(secrets),
        },
        memoryLimitMiB: 512, // Default is 512
        publicLoadBalancer: true, // Default is false
        domainZone: this.hostedZone,
        certificate: this.certificate,
      },
    );

    const scalableTarget = this.ecsService.service.autoScaleTaskCount({
      minCapacity: props.scaling.desiredCount,
      maxCapacity: props.scaling.maxCount,
    });

    scalableTarget.scaleOnCpuUtilization("cpu-scaling", {
      targetUtilizationPercent: props.scaling.cpuPercentage,
    });
    scalableTarget.scaleOnMemoryUtilization("memory-scaling", {
      targetUtilizationPercent: props.scaling.memoryPercentage,
    });

    secrets.grantRead(this.ecsService.taskDefinition.taskRole);
  }

  private setUpAliasRecord(props: ApiStackProps) {
    this.gatewayUrl = new CfnOutput(this, "gateway-url-output", {
      value: this.ecsService.loadBalancer.loadBalancerDnsName,
    });

    this.aliasRecord = new Route53.ARecord(this, "alias-record", {
      zone: this.hostedZone,
      recordName: props.domainName,
      target: Route53.RecordTarget.fromAlias(
        new Route53Targets.LoadBalancerTarget(this.ecsService.loadBalancer),
      ),
    });

    const shouldCreateWWW = props.domainName.split(".").length === 2;
    if (shouldCreateWWW) {
      new Route53.ARecord(this, "alias-record-www", {
        zone: this.hostedZone,
        recordName: `www.${props.domainName}`,
        target: Route53.RecordTarget.fromAlias(
          new Route53Targets.LoadBalancerTarget(this.ecsService.loadBalancer),
        ),
      });
    }
  }
}
</code>```

Any advice is greatly appreciated.

答:
I would strongly advise moving from the Docker directory to ECR public gallery to avoid ratelimit issues: https://gallery.ecr.aws/

That said, to answer the question about the number of NATs created. As you can see in the CDK docs, what you're seeing reflects the default behavior (emphasis mine):

A VPC consists of one or more subnets that instances can be placed
into. CDK distinguishes three different subnet types:

Public (SubnetType.PUBLIC) - public subnets connect directly to the Internet using an Internet Gateway. If you want your instances to
have a public IP address and be directly reachable from the Internet,
you must place them in a public subnet.

Private with Internet Access (SubnetType.PRIVATE_WITH_NAT) - instances in private subnets are not directly routable from the
Internet, and connect out to the Internet via a NAT gateway. By
default, a NAT gateway is created in every public subnet for maximum
availability. Be aware that you will be charged for NAT gateways.

Isolated (SubnetType.PRIVATE_ISOLATED) - isolated subnets do not route from or to the Internet, and as such do not require NAT
gateways. They can only connect to or be connected to from other
instances in the same VPC. A default VPC configuration will not
include isolated subnets,

A default VPC configuration will create public and private subnets.
However, if natGateways:0 and subnetConfiguration is undefined,
default VPC configuration will create public and isolated subnets.

So a separate NAT is created for every Public subnet.

Also, the docs for the natGateways parameter mentioned above also describe the default behavior:

(default: One NAT gateway/instance per Availability Zone)

To limit the number of AZs used by the VPC, specify the maxAzs parameter. Set it to 1 to only have a single NAT per VPC.

If you're fine with making the resources in the VPC publicly reachable from the internet, you can place them in Public subnets and avoid the creation of NATs altogether.

```<code>this.vpc = new EC2.Vpc(this, this.resourceName(props, "vpc"), {
  maxAzs: 1,
  natGateways: 0;
});
</code>```

If you do this, you have to tell your resources to use the public subnet instead of the isolated one.

However, CodeBuild projects do not support this.

They require a NAT to connect to the internet if placed into a VPC. See this question for details.

So if you want your build project to be in a VPC, you need to place it into a private subnet. This is done by default, so no additional configuration needed. Just make sure you have at least one NAT gateway.

To sum up, the real solution to the Docker Hub rate limit issue is to switch over to ECR Public gallery.

Body:
I recently set up an application on AWS via CDK. The application consists of a Dockerized nodejs application, which connects to an RDS instance, and has a Redis caching layer as well. After having the application deployed for a few days, the costs are much higher than I had anticipated, even with minimal traffic. After looking through the cost explorer, it looks like half of the cost is coming from the NAT gateways.

In my current setup, I have created two VPCs. One is used for the application stack, and the other is for the CodePipeline. I needed to add one for the pipeline because without it I was hitting rate limits when trying to pull Docker images during the CodeBuildAction steps.

I'm not very comfortable with the networking bits, but I feel like there are extra resources involved. The pipeline VPC has three NAT gateways and three EIPs. These end up just sitting there waiting for the next deployment, which seems like a huge waste. It seems like a new gateway + EIP is allocated for each construct the VPC is attached to in CDK. Can I just make it reuse the same one? Is there an alternative to adding a VPC at all and not getting rate limited by Docker?

I also find it very surprising (I might just be naive) that the NAT gateway is so far equally as expensive as my current Fargate task costs. Is there an alternative that would serve my purposes, but come at a little lower cost?

Anyways, here are my two stacks:

// pipeline-stack.ts

import { SecretValue, Stack, StackProps } from "aws-cdk-lib";
import { Construct } from "constructs";
import { Artifact, IStage, Pipeline } from "aws-cdk-lib/aws-codepipeline";
import {
  CloudFormationCreateUpdateStackAction,
  CodeBuildAction,
  CodeBuildActionType,
  GitHubSourceAction,
} from "aws-cdk-lib/aws-codepipeline-actions";
import {
  BuildEnvironmentVariableType,
  BuildSpec,
  LinuxBuildImage,
  PipelineProject,
} from "aws-cdk-lib/aws-codebuild";
import { SnsTopic } from "aws-cdk-lib/aws-events-targets";
import { Topic } from "aws-cdk-lib/aws-sns";
import { EventField, RuleTargetInput } from "aws-cdk-lib/aws-events";
import { EmailSubscription, SmsSubscription } from "aws-cdk-lib/aws-sns-subscriptions";
import ApiStack from "./stacks/api-stack";
import { ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
import { Secret } from "aws-cdk-lib/aws-ecs";
import { BuildEnvironmentVariable } from "aws-cdk-lib/aws-codebuild/lib/project";
import * as SecretsManager from "aws-cdk-lib/aws-secretsmanager";
import { getApplicationEnvironment, getApplicationSecrets } from "./secrets-helper";

const capFirst = (str: string): string => {
  return str.charAt(0).toUpperCase() + str.slice(1);
};

interface PipelineStackProps extends StackProps {
  environment: string;
  emailAddress: string;
  phoneNumber: string;
  branch: string;
  secrets: {
    arn: string;
  };
  repo: {
    uri: string;
    name: string;
  };
}

export class PipelineStack extends Stack {
  private readonly envName: string;
  private readonly pipeline: Pipeline;

  // source outputs
  private cdkSourceOutput: Artifact;
  private applicationSourceOutput: Artifact;

  // code source actions
  private cdkSourceAction: GitHubSourceAction;
  private applicationSourceAction: GitHubSourceAction;

  // build outputs
  private cdkBuildOutput: Artifact;
  private applicationBuildOutput: Artifact;

  // notifications
  private pipelineNotificationsTopic: Topic;

  private readonly codeBuildVpc: Vpc;
  private readonly codeBuildSecurityGroup: SecurityGroup;

  private readonly secrets: SecretsManager.ISecret;
  private readonly ecrCodeBuildRole: Role;

  // stages
  private sourceStage: IStage;
  private selfMutateStage: IStage;
  private buildStage: IStage;
  private apiTestsStage: IStage;

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

    this.envName = props.environment;
    this.addNotifications(props);

    this.ecrCodeBuildRole = new Role(this, "application-build-project-role", {
      assumedBy: new ServicePrincipal("codebuild.amazonaws.com"),
      managedPolicies: [
        ManagedPolicy.fromAwsManagedPolicyName("AmazonEC2ContainerRegistryPowerUser"),
      ],
    });

    this.codeBuildVpc = new Vpc(this, "codebuild-vpc", {
      vpcName: "codebuild-vpc",
      enableDnsSupport: true,
    });
    this.codeBuildSecurityGroup = new SecurityGroup(this, "codebuild-vpc-security-group", {
      vpc: this.codeBuildVpc,
      allowAllOutbound: true,
    });

    this.secrets = SecretsManager.Secret.fromSecretCompleteArn(this, "secrets", props.secrets.arn);

    this.pipeline = new Pipeline(this, "pipeline", {
      pipelineName: `${capFirst(this.envName)}Pipeline`,
      crossAccountKeys: false,
      restartExecutionOnUpdate: true,
    });

    // STAGE 1 - Source Stage
    this.addSourceStage(props);

    // STAGE 2 - Build Stage
    this.addBuildStage(props);

    // STAGE 3: SelfMutate Stage
    this.addSelfMutateStage();

    // STAGE 4: Testing
    this.addTestStage();
  }

  addNotifications(props: PipelineStackProps) {
    this.pipelineNotificationsTopic = new Topic(this, "pipeline-notifications-topic", {
      topicName: `PipelineNotifications${capFirst(props.environment)}`,
    });
    this.pipelineNotificationsTopic.addSubscription(new EmailSubscription(props.emailAddress));
    this.pipelineNotificationsTopic.addSubscription(new SmsSubscription(props.phoneNumber));
  }

  /**
   * Stage 1
   */
  addSourceStage(props: PipelineStackProps) {
    this.cdkSourceOutput = new Artifact("cdk-source-output");
    this.cdkSourceAction = new GitHubSourceAction({
      actionName: "CdkSource",
      owner: "my-org",
      repo: "my-cdk-repo",
      branch: "main",
      oauthToken: SecretValue.secretsManager("/connections/github/access-token"),
      output: this.cdkSourceOutput,
    });

    this.applicationSourceOutput = new Artifact("ApplicationSourceOutput");
    this.applicationSourceAction = new GitHubSourceAction({
      actionName: "ApplicationSource",
      owner: "my-org",
      repo: "my-application-repo",
      branch: props.branch,
      oauthToken: SecretValue.secretsManager("/connections/github/access-token"),
      output: this.applicationSourceOutput,
    });

    this.sourceStage = this.pipeline.addStage({
      stageName: "Source",
      actions: [this.cdkSourceAction, this.applicationSourceAction],
    });
  }

  /**
   * stage 2
   */
  addBuildStage(props: PipelineStackProps) {
    const cdkBuildAction = this.createCdkBuildAction();
    const applicationBuildAction = this.createApplicationBuildAction(props);
    this.buildStage = this.pipeline.addStage({
      stageName: "Build",
      actions: [cdkBuildAction, applicationBuildAction],
    });
  }

  /**
   * stage 3
   */
  addSelfMutateStage() {
    this.selfMutateStage = this.pipeline.addStage({
      stageName: "PipelineUpdate",
      actions: [
        new CloudFormationCreateUpdateStackAction({
          actionName: "PipelineCreateUpdateStackAction",
          stackName: this.stackName,
          templatePath: this.cdkBuildOutput.atPath(`${this.stackName}.template.json`),
          adminPermissions: true,
        }),
      ],
    });
  }

  /**
   * stage 4
   */
  addTestStage() {
    const testAction = new CodeBuildAction({
      actionName: "RunApiTests",
      type: CodeBuildActionType.TEST,
      input: this.applicationSourceOutput,
      project: new PipelineProject(this, "api-tests-project", {
        vpc: this.codeBuildVpc,
        securityGroups: [this.codeBuildSecurityGroup],
        environment: {
          buildImage: LinuxBuildImage.STANDARD_5_0,
          privileged: true,
        },
        buildSpec: BuildSpec.fromObject({
          version: "0.2",
          phases: {
            install: {
              commands: ["cp .env.testing .env"],
            },
            build: {
              commands: [
                "ls",
                "docker-compose -f docker-compose.staging.yml run -e NODE_ENV=testing --rm api node ace test",
              ],
            },
          },
        }),
      }),
      runOrder: 1,
    });
    this.apiTestsStage = this.pipeline.addStage({
      stageName: "RunApiTests",
      actions: [testAction],
    });
  }

  createCdkBuildAction() {
    this.cdkBuildOutput = new Artifact("CdkBuildOutput");
    return new CodeBuildAction({
      actionName: "CdkBuildAction",
      input: this.cdkSourceOutput,
      outputs: [this.cdkBuildOutput],
      project: new PipelineProject(this, "cdk-build-project", {
        environment: {
          buildImage: LinuxBuildImage.STANDARD_5_0,
        },
        buildSpec: BuildSpec.fromSourceFilename("build-specs/cdk-build-spec.yml"),
      }),
    });
  }

  createApplicationBuildAction(props: PipelineStackProps) {
    this.applicationBuildOutput = new Artifact("ApplicationBuildOutput");

    const project = new PipelineProject(this, "application-build-project", {
      vpc: this.codeBuildVpc,
      securityGroups: [this.codeBuildSecurityGroup],
      environment: {
        buildImage: LinuxBuildImage.STANDARD_5_0,
        privileged: true,
      },
      environmentVariables: {
        ENV: {
          value: this.envName,
        },
        ECR_REPO_URI: {
          value: props.repo.uri,
        },
        ECR_REPO_NAME: {
          value: props.repo.name,
        },
        AWS_REGION: {
          value: props.env!.region,
        },
      },
      buildSpec: BuildSpec.fromObject({
        version: "0.2",
        phases: {
          pre_build: {
            commands: [
              "echo 'Logging into Amazon ECR...'",
              "aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO_URI",
              'COMMIT_HASH=$(echo "$CODEBUILD_RESOLVED_SOURCE_VERSION" | head -c 8)',
            ],
          },
          build: {
            commands: ["docker build -t $ECR_REPO_NAME:latest ."],
          },
          post_build: {
            commands: [
              "docker tag $ECR_REPO_NAME:latest $ECR_REPO_URI/$ECR_REPO_NAME:latest",
              "docker tag $ECR_REPO_NAME:latest $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH",
              "docker push $ECR_REPO_URI/$ECR_REPO_NAME:latest",
              "docker push $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH",
            ],
          },
        },
      }),
      role: this.ecrCodeBuildRole,
    });

    return new CodeBuildAction({
      actionName: "ApplicationBuildAction",
      input: this.applicationSourceOutput,
      outputs: [this.applicationBuildOutput],
      project: project,
    });
  }

  public addDatabaseMigrationStage(apiStack: ApiStack, stageName: string): IStage {
    let buildEnv: { [name: string]: BuildEnvironmentVariable } = {
      ENV: {
        value: this.envName,
      },
      ECR_REPO_URI: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: apiStack.repoUri,
      },
      ECR_REPO_NAME: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: apiStack.repoName,
      },
      AWS_REGION: {
        type: BuildEnvironmentVariableType.PLAINTEXT,
        value: this.region,
      },
    };
    buildEnv = this.getBuildEnvAppSecrets(getApplicationSecrets(this.secrets), buildEnv);
    buildEnv = this.getBuildEnvAppEnvVars(
      getApplicationEnvironment({
        REDIS_HOST: apiStack.redisHost.importValue,
        REDIS_PORT: apiStack.redisPort.importValue,
      }),
      buildEnv,
    );

    let envVarNames = Object.keys(buildEnv);

    const envFileCommand = `printenv | grep '${envVarNames.join("\\|")}' >> .env`;

    return this.pipeline.addStage({
      stageName: stageName,
      actions: [
        new CodeBuildAction({
          actionName: "DatabaseMigrations",
          input: this.applicationSourceOutput,
          project: new PipelineProject(this, "database-migrations-project", {
            description: "Run database migrations against RDS database",
            environment: {
              buildImage: LinuxBuildImage.STANDARD_5_0,
              privileged: true,
            },
            environmentVariables: buildEnv,
            buildSpec: BuildSpec.fromObject({
              version: "0.2",
              phases: {
                pre_build: {
                  commands: [
                    "echo 'Logging into Amazon ECR...'",
                    "aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO_URI",
                    'COMMIT_HASH=$(echo "$CODEBUILD_RESOLVED_SOURCE_VERSION" | head -c 8)',
                    envFileCommand,
                    "cat .env",
                  ],
                },
                build: {
                  commands: [
                    `docker run --env-file .env --name api $ECR_REPO_URI/$ECR_REPO_NAME:$ENV-$COMMIT_HASH node ace migration:run --force`,
                    ": > .env",
                  ],
                },
              },
            }),
            role: this.ecrCodeBuildRole,
          }),
        }),
      ],
    });
  }

  private getBuildEnvAppSecrets(
    secrets: { [key: string]: Secret },
    buildEnv: { [name: string]: BuildEnvironmentVariable },
  ): { [name: string]: BuildEnvironmentVariable } {
    for (let key in secrets) {
      buildEnv[key] = {
        type: BuildEnvironmentVariableType.SECRETS_MANAGER,
        value: `${this.secrets.secretArn}:${key}`,
      };
    }

    return buildEnv;
  }

  private getBuildEnvAppEnvVars(
    vars: { [key: string]: string },
    buildEnv: { [name: string]: BuildEnvironmentVariable },
  ): { [name: string]: BuildEnvironmentVariable } {
    for (let key in vars) {
      buildEnv[key] = {
        value: vars[key],
      };
    }

    return buildEnv;
  }

  public addApplicationStage(apiStack: ApiStack, stageName: string): IStage {
    return this.pipeline.addStage({
      stageName: stageName,
      actions: [
        new CloudFormationCreateUpdateStackAction({
          actionName: "ApplicationUpdate",
          stackName: apiStack.stackName,
          templatePath: this.cdkBuildOutput.atPath(`${apiStack.stackName}.template.json`),
          adminPermissions: true,
        }),
      ],
    });
  }
}

// api-stack.ts

import { CfnOutput, CfnResource, Lazy, Stack, StackProps } from "aws-cdk-lib";
import * as EC2 from "aws-cdk-lib/aws-ec2";
import { ISubnet } from "aws-cdk-lib/aws-ec2";
import * as ECS from "aws-cdk-lib/aws-ecs";
import { DeploymentControllerType, ScalableTaskCount } from "aws-cdk-lib/aws-ecs";
import * as EcsPatterns from "aws-cdk-lib/aws-ecs-patterns";
import * as RDS from "aws-cdk-lib/aws-rds";
import { Credentials } from "aws-cdk-lib/aws-rds";
import * as Route53 from "aws-cdk-lib/aws-route53";
import * as Route53Targets from "aws-cdk-lib/aws-route53-targets";
import * as ECR from "aws-cdk-lib/aws-ecr";
import * as CertificateManager from "aws-cdk-lib/aws-certificatemanager";
import * as SecretsManager from "aws-cdk-lib/aws-secretsmanager";
import * as ElasticCache from "aws-cdk-lib/aws-elasticache";
import { Construct } from "constructs";
import { getApplicationEnvironment, getApplicationSecrets } from "../secrets-helper";

export type ApiStackProps = StackProps & {
  environment: string;
  hostedZone: {
    id: string;
    name: string;
  };
  domainName: string;
  scaling: {
    desiredCount: number;
    maxCount: number;
    cpuPercentage: number;
    memoryPercentage: number;
  };
  repository: {
    uri: string;
    arn: string;
    name: string;
  };
  secrets: { arn: string };
};

export default class ApiStack extends Stack {
  vpc: EC2.Vpc;
  cluster: ECS.Cluster;
  ecsService: EcsPatterns.ApplicationLoadBalancedFargateService;
  certificate: CertificateManager.ICertificate;
  repository: ECR.IRepository;
  database: RDS.IDatabaseInstance;
  databaseCredentials: Credentials;
  hostedZone: Route53.IHostedZone;
  aliasRecord: Route53.ARecord;
  redis: ElasticCache.CfnReplicationGroup;

  repoUri: string;
  repoName: string;

  applicationEnvVariables: {
    [key: string]: string;
  };

  redisHost: CfnOutput;
  redisPort: CfnOutput;
  gatewayUrl: CfnOutput;

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

    this.repoUri = props.repository.uri;
    this.repoName = props.repository.name;

    this.setUpVpc(props);
    this.setUpRedisCluster(props);
    this.setUpDatabase(props);
    this.setUpCluster(props);
    this.setUpHostedZone(props);
    this.setUpCertificate(props);
    this.setUpRepository(props);
    this.setUpEcsService(props);
    this.setUpAliasRecord(props);
  }

  private resourceName(props: ApiStackProps, resourceType: string): string {
    return `twibs-api-${resourceType}-${props.environment}`;
  }

  private setUpVpc(props: ApiStackProps) {
    this.vpc = new EC2.Vpc(this, this.resourceName(props, "vpc"), {
      maxAzs: 3, // Default is all AZs in region
    });
  }

  private setUpRedisCluster(props: ApiStackProps) {
    const subnetGroup = new ElasticCache.CfnSubnetGroup(this, "cache-subnet-group", {
      cacheSubnetGroupName: "redis-cache-subnet-group",
      subnetIds: this.vpc.privateSubnets.map((subnet: ISubnet) => subnet.subnetId),
      description: "Subnet group for Redis Cache cluster",
    });

    const securityGroup = new EC2.SecurityGroup(this, "redis-security-group", {
      vpc: this.vpc,
      description: `SecurityGroup associated with RedisDB Cluster - ${props.environment}`,
      allowAllOutbound: false,
    });

    securityGroup.addIngressRule(
      EC2.Peer.ipv4(this.vpc.vpcCidrBlock),
      EC2.Port.tcp(6379),
      "Allow from VPC on port 6379",
    );

    this.redis = new ElasticCache.CfnReplicationGroup(this, "redis", {
      numNodeGroups: 1,
      cacheNodeType: "cache.t2.small",
      engine: "redis",
      multiAzEnabled: false,
      autoMinorVersionUpgrade: false,
      cacheParameterGroupName: "default.redis6.x.cluster.on",
      engineVersion: "6.x",
      cacheSubnetGroupName: subnetGroup.ref,
      securityGroupIds: [securityGroup.securityGroupId],
      replicationGroupDescription: "RedisDB setup by CDK",
      replicasPerNodeGroup: 0,
      port: 6379,
    });
  }

  private setUpDatabase(props: ApiStackProps) {
    if (["production", "staging", "develop"].includes(props.environment)) {
      return;
    }

    this.databaseCredentials = Credentials.fromUsername("my_db_username");
    this.database = new RDS.DatabaseInstance(this, "database", {
      vpc: this.vpc,
      engine: RDS.DatabaseInstanceEngine.postgres({
        version: RDS.PostgresEngineVersion.VER_13_4,
      }),
      credentials: this.databaseCredentials,
      databaseName: `my_app_${props.environment}`,
      deletionProtection: true,
    });
  }

  private setUpCluster(props: ApiStackProps) {
    this.cluster = new ECS.Cluster(this, this.resourceName(props, "cluster"), {
      vpc: this.vpc,
      capacity: {
        instanceType: EC2.InstanceType.of(EC2.InstanceClass.T3, EC2.InstanceSize.`SMALL`),
      },
    });
  }

  private setUpHostedZone(props: ApiStackProps) {
    this.hostedZone = Route53.HostedZone.fromHostedZoneAttributes(
      this,
      this.resourceName(props, "hosted-zone"),
      {
        hostedZoneId: props.hostedZone.id,
        zoneName: props.hostedZone.name,
      },
    );
  }

  private setUpCertificate(props: ApiStackProps) {
    this.certificate = new CertificateManager.Certificate(this, "certificate", {
      domainName: props.domainName,
      validation: CertificateManager.CertificateValidation.fromDns(this.hostedZone),
    });
  }

  private setUpRepository(props: ApiStackProps) {
    this.repository = ECR.Repository.fromRepositoryAttributes(
      this,
      this.resourceName(props, "repository"),
      {
        repositoryArn: props.repository.arn,
        repositoryName: props.repository.name,
      },
    );
  }

  private setUpEcsService(props: ApiStackProps) {
    const secrets = SecretsManager.Secret.fromSecretCompleteArn(this, "secrets", props.secrets.arn);
    this.redisHost = new CfnOutput(this, "redis-host-output", {
      value: this.redis.attrConfigurationEndPointAddress,
      exportName: "redis-host-output",
    });
    this.redisPort = new CfnOutput(this, "redis-port-output", {
      value: this.redis.attrConfigurationEndPointPort,
      exportName: "redis-port-output",
    });

    // Create a load-balanced ecs-service service and make it public
    this.ecsService = new EcsPatterns.ApplicationLoadBalancedFargateService(
      this,
      this.resourceName(props, "ecs-service"),
      {
        serviceName: `${props.environment}-api-service`,
        cluster: this.cluster, // Required
        cpu: 256, // Default is 256
        desiredCount: props.scaling.desiredCount, // Default is 1
        taskImageOptions: {
          image: ECS.ContainerImage.fromEcrRepository(this.repository),
          environment: getApplicationEnvironment({
            REDIS_HOST: this.redis.attrConfigurationEndPointAddress,
            REDIS_PORT: this.redis.attrConfigurationEndPointPort,
          }),
          secrets: getApplicationSecrets(secrets),
        },
        memoryLimitMiB: 512, // Default is 512
        publicLoadBalancer: true, // Default is false
        domainZone: this.hostedZone,
        certificate: this.certificate,
      },
    );

    const scalableTarget = this.ecsService.service.autoScaleTaskCount({
      minCapacity: props.scaling.desiredCount,
      maxCapacity: props.scaling.maxCount,
    });

    scalableTarget.scaleOnCpuUtilization("cpu-scaling", {
      targetUtilizationPercent: props.scaling.cpuPercentage,
    });
    scalableTarget.scaleOnMemoryUtilization("memory-scaling", {
      targetUtilizationPercent: props.scaling.memoryPercentage,
    });

    secrets.grantRead(this.ecsService.taskDefinition.taskRole);
  }

  private setUpAliasRecord(props: ApiStackProps) {
    this.gatewayUrl = new CfnOutput(this, "gateway-url-output", {
      value: this.ecsService.loadBalancer.loadBalancerDnsName,
    });

    this.aliasRecord = new Route53.ARecord(this, "alias-record", {
      zone: this.hostedZone,
      recordName: props.domainName,
      target: Route53.RecordTarget.fromAlias(
        new Route53Targets.LoadBalancerTarget(this.ecsService.loadBalancer),
      ),
    });

    const shouldCreateWWW = props.domainName.split(".").length === 2;
    if (shouldCreateWWW) {
      new Route53.ARecord(this, "alias-record-www", {
        zone: this.hostedZone,
        recordName: `www.${props.domainName}`,
        target: Route53.RecordTarget.fromAlias(
          new Route53Targets.LoadBalancerTarget(this.ecsService.loadBalancer),
        ),
      });
    }
  }
}

Any advice is greatly appreciated.

Body:
I would strongly advise moving from the Docker directory to ECR public gallery to avoid ratelimit issues: https://gallery.ecr.aws/

That said, to answer the question about the number of NATs created. As you can see in the CDK docs, what you're seeing reflects the default behavior (emphasis mine):

A VPC consists of one or more subnets that instances can be placed
into. CDK distinguishes three different subnet types:

Public (SubnetType.PUBLIC) - public subnets connect directly to the Internet using an Internet Gateway. If you want your instances to
have a public IP address and be directly reachable from the Internet,
you must place them in a public subnet.

Private with Internet Access (SubnetType.PRIVATE_WITH_NAT) - instances in private subnets are not directly routable from the
Internet, and connect out to the Internet via a NAT gateway. By
default, a NAT gateway is created in every public subnet for maximum
availability. Be aware that you will be charged for NAT gateways.

Isolated (SubnetType.PRIVATE_ISOLATED) - isolated subnets do not route from or to the Internet, and as such do not require NAT
gateways. They can only connect to or be connected to from other
instances in the same VPC. A default VPC configuration will not
include isolated subnets,

A default VPC configuration will create public and private subnets.
However, if natGateways:0 and subnetConfiguration is undefined,
default VPC configuration will create public and isolated subnets.

So a separate NAT is created for every Public subnet.

Also, the docs for the natGateways parameter mentioned above also describe the default behavior:

(default: One NAT gateway/instance per Availability Zone)

To limit the number of AZs used by the VPC, specify the maxAzs parameter. Set it to 1 to only have a single NAT per VPC.

If you're fine with making the resources in the VPC publicly reachable from the internet, you can place them in Public subnets and avoid the creation of NATs altogether.

this.vpc = new EC2.Vpc(this, this.resourceName(props, "vpc"), {
  maxAzs: 1,
  natGateways: 0;
});

If you do this, you have to tell your resources to use the public subnet instead of the isolated one.

However, CodeBuild projects do not support this.

They require a NAT to connect to the internet if placed into a VPC. See this question for details.

So if you want your build project to be in a VPC, you need to place it into a private subnet. This is done by default, so no additional configuration needed. Just make sure you have at least one NAT gateway.

To sum up, the real solution to the Docker Hub rate limit issue is to switch over to ECR Public gallery.


来源: stackexchange

问:
How to center ElevatedButton in flutter?
I imported [sliding up panel][2] from pub.dev and everything is good except for one issue. I'm looking for a way to center the 'blue circle' vertically. (in between 'red appbar' and 'buttom slider') I tried wraping the code with 'center' or using mainaxisalignment but it doesn't work. Other things I tried which I found on google just gave me an errors. Please help.
Thanks in advance

```<code>return Scaffold(
  appBar: PreferredSize(
    preferredSize: Size.fromHeight(200),
    child: AppBar(
      backgroundColor: Colors.pinkAccent,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(bottom: Radius.circular(100))
      ),
      flexibleSpace: Container(
        alignment: Alignment.center,
        child: Text(
          'Red AppBar',
          style: TextStyle(
              color: Colors.white,
              fontSize: 50.0,
              fontWeight: FontWeight.bold
          ),
        ),
      ),
    ),
  ),
  body: SlidingUpPanel(
    renderPanelSheet: false,
    panel: _floatingPanel(),
    collapsed: _floatingCollapsed(),
    body: ElevatedButton(
      onPressed: () {},
      child: Text('Blue Circle'),
      style: ElevatedButton.styleFrom(
        shape: CircleBorder(),
        padding: EdgeInsets.all(20),
      ),
    )
  ),
);

  Widget _floatingCollapsed() {
return Container(
  decoration: BoxDecoration(
    color: Colors.blueGrey,
    borderRadius: BorderRadius.only(
        topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
  ),
  margin: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
  child: Center(
    child: Text(
      "buttom slider",
      style: TextStyle(color: Colors.white),
    ),
  ),
);
</code>```

}

```<code>  Widget _floatingPanel() {
return Container(
  decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.all(Radius.circular(24.0)),
      boxShadow: [
        BoxShadow(
          blurRadius: 20.0,
          color: Colors.grey,
        ),
      ]
  ),
  margin: const EdgeInsets.all(24.0),
  child: Center(
    child: Text("LEVEL INFO"),
  ),
);
</code>```

}

Also, what could be the best way to give margin to the 'blue circle' so it won't touch the far left and far right side of the screen?

答:
As per the documentation of sliding_up_panel, the body represents the widget that lies underneath the sliding panel and it automatically sizes itself to fill the screen. So it occupies full screen. Since you expanded AppBar to 200 unit, the body Widget was pushed down, hence you are seeing the centre point also pushed down by 200 unit. If you change the body height to occupy full screen from the start, by setting the Scaffold property extendBodyBehindAppBar with true, you will place the widget in the correct centre point.

if you add the below line immediately after the Scaffold. You will get desired result.

```<code>return Scaffold(
   extendBodyBehindAppBar:true,
</code>```

ContentLicense: CC BY-SA 4.0

Body:
I imported [sliding up panel][2] from pub.dev and everything is good except for one issue. I'm looking for a way to center the 'blue circle' vertically. (in between 'red appbar' and 'buttom slider') I tried wraping the code with 'center' or using mainaxisalignment but it doesn't work. Other things I tried which I found on google just gave me an errors. Please help.
Thanks in advance

return Scaffold(
  appBar: PreferredSize(
    preferredSize: Size.fromHeight(200),
    child: AppBar(
      backgroundColor: Colors.pinkAccent,
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.vertical(bottom: Radius.circular(100))
      ),
      flexibleSpace: Container(
        alignment: Alignment.center,
        child: Text(
          'Red AppBar',
          style: TextStyle(
              color: Colors.white,
              fontSize: 50.0,
              fontWeight: FontWeight.bold
          ),
        ),
      ),
    ),
  ),
  body: SlidingUpPanel(
    renderPanelSheet: false,
    panel: _floatingPanel(),
    collapsed: _floatingCollapsed(),
    body: ElevatedButton(
      onPressed: () {},
      child: Text('Blue Circle'),
      style: ElevatedButton.styleFrom(
        shape: CircleBorder(),
        padding: EdgeInsets.all(20),
      ),
    )
  ),
);

  Widget _floatingCollapsed() {
return Container(
  decoration: BoxDecoration(
    color: Colors.blueGrey,
    borderRadius: BorderRadius.only(
        topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
  ),
  margin: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 0.0),
  child: Center(
    child: Text(
      "buttom slider",
      style: TextStyle(color: Colors.white),
    ),
  ),
);

}

  Widget _floatingPanel() {
return Container(
  decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.all(Radius.circular(24.0)),
      boxShadow: [
        BoxShadow(
          blurRadius: 20.0,
          color: Colors.grey,
        ),
      ]
  ),
  margin: const EdgeInsets.all(24.0),
  child: Center(
    child: Text("LEVEL INFO"),
  ),
);

}

Also, what could be the best way to give margin to the 'blue circle' so it won't touch the far left and far right side of the screen?

Body:
As per the documentation of sliding_up_panel, the body represents the widget that lies underneath the sliding panel and it automatically sizes itself to fill the screen. So it occupies full screen. Since you expanded AppBar to 200 unit, the body Widget was pushed down, hence you are seeing the centre point also pushed down by 200 unit. If you change the body height to occupy full screen from the start, by setting the Scaffold property extendBodyBehindAppBar with true, you will place the widget in the correct centre point.

if you add the below line immediately after the Scaffold. You will get desired result.

return Scaffold(
   extendBodyBehindAppBar:true,


问:
Invalid 'departure_time' parameter when using Google distance matrix api
I want to calculate the drive time using the distance matrix API - while doing so I need to calculate the drive time at specific times of the day and therefore I'm using the departure_time parameter. However, I can't seem to figure out the correct format that I need to specify the time in. When I set departure_time=now in the API URL works fine. But when I try to specify a time, say 1pm, I get an error Invalid request. Invalid departure_time parameter.

For example, the following URL works fine (API key removed) - https://maps.googleapis.com/maps/api/distancematrix/json?origins=53.542416,-1.437382&destinations=54.552902,-6.000198999999999&key=XXXX&mode=driving&departure_time=now

But this one doesn't - https://maps.googleapis.com/maps/api/distancematrix/json?origins=53.542416,-1.437382&destinations=54.552902,-6.000198999999999&key=XXXX&mode=driving&departure_time=2021-09-21T13:00:00.00000Z

Can someone please advise on what's the best way to achieve this? Thanks

答:
The parameters arrival_time and departure_time should be given as a timestamp, seconds after 1970-01-01 00:00:00.

E.g. in python this is done by calling date.timestamp() on a datetime-object.


问:
Can we make Single JDBC Sink Connector for multiple source db if primary key is same in all source DB?
Below is my JDBC Sink Connector Configuration Properties.

```<code>    "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector",
    "transforms.dropPrefix.replacement": "$1",
    "table.name.format": "kafka_${topic}",
    "connection.password": "********",
    "tasks.max": "3",
    "topics": "aiq.db1.Test1,aiq.db1.Test2,aiq.db2.Topic1,aiq.db2.Topic2",
    "batch.size": "3000",
    "transforms": "dropPrefix",
    "transforms.dropPrefix.regex": "aiq.(.*)",
    "transforms.dropPrefix.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "value.converter.schema.registry.url": "http://localhost:8081",
    "value.converter.value.subject.name.strategy": "io.confluent.kafka.serializers.subject.RecordNameStrategy",
    "auto.evolve": "true",
    "connection.user": "admin",
    "name": "MSSQL_jdbc_sink_connect",
    "errors.tolerance": "all",
    "auto.create": "true",
    "value.converter": "io.confluent.connect.avro.AvroConverter",
    "connection.url": "jdbc:sqlserver://mssql",
    "insert.mode": "upsert",
    "key.converter": "io.confluent.connect.avro.AvroConverter",
    "key.converter.schema.registry.url": "http://localhost:8081",
    "pk.mode": "record_value",
    "pk.fields": "id"
</code>```

If I used this then connector is looking for db1 or db2, which is source db and giving this error.

```<code>com.microsoft.sqlserver.jdbc.SQLServerException: Database 'db2' does not exist. Make sure that the name is entered correctly.

at io.confluent.connect.jdbc.sink.JdbcSinkTask.getAllMessagesException(JdbcSinkTask.java:150)
at io.confluent.connect.jdbc.sink.JdbcSinkTask.put(JdbcSinkTask.java:102)
... 11 more
[2022-01-25 06:09:09,582] WARN Write of 500 records failed, remainingRetries=10 (io.confluent.connect.jdbc.sink.JdbcSinkTask:92)
com.microsoft.sqlserver.jdbc.SQLServerException: Database 'db2' does not exist. Make sure that the name is entered correctly.
</code>```

Please let me know Can I create a JDBC sink connector which uses more than one databases for source topic.
If this scenario is possible then How can I achieve this by using JDBC Sink Connector?

答:
I have used these properties and it worked for me in this case(if we have multiple source db and one target db to store that data)</p>
```<code>table.name.format=iq_${topic}
transforms=dropPrefix
transforms.dropPrefix.replacement=$1_transferiq_$2
transforms.dropPrefix.regex=iq.(.*).transferiq.(.*)
transforms.dropPrefix.type=org.apache.kafka.connect.transforms.RegexRouter
</code>```

Body:
Below is my JDBC Sink Connector Configuration Properties.

    "connector.class": "io.confluent.connect.jdbc.JdbcSinkConnector",
    "transforms.dropPrefix.replacement": "$1",
    "table.name.format": "kafka_${topic}",
    "connection.password": "********",
    "tasks.max": "3",
    "topics": "aiq.db1.Test1,aiq.db1.Test2,aiq.db2.Topic1,aiq.db2.Topic2",
    "batch.size": "3000",
    "transforms": "dropPrefix",
    "transforms.dropPrefix.regex": "aiq.(.*)",
    "transforms.dropPrefix.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "value.converter.schema.registry.url": "http://localhost:8081",
    "value.converter.value.subject.name.strategy": "io.confluent.kafka.serializers.subject.RecordNameStrategy",
    "auto.evolve": "true",
    "connection.user": "admin",
    "name": "MSSQL_jdbc_sink_connect",
    "errors.tolerance": "all",
    "auto.create": "true",
    "value.converter": "io.confluent.connect.avro.AvroConverter",
    "connection.url": "jdbc:sqlserver://mssql",
    "insert.mode": "upsert",
    "key.converter": "io.confluent.connect.avro.AvroConverter",
    "key.converter.schema.registry.url": "http://localhost:8081",
    "pk.mode": "record_value",
    "pk.fields": "id"

If I used this then connector is looking for db1 or db2, which is source db and giving this error.

com.microsoft.sqlserver.jdbc.SQLServerException: Database 'db2' does not exist. Make sure that the name is entered correctly.

at io.confluent.connect.jdbc.sink.JdbcSinkTask.getAllMessagesException(JdbcSinkTask.java:150)
at io.confluent.connect.jdbc.sink.JdbcSinkTask.put(JdbcSinkTask.java:102)
... 11 more
[2022-01-25 06:09:09,582] WARN Write of 500 records failed, remainingRetries=10 (io.confluent.connect.jdbc.sink.JdbcSinkTask:92)
com.microsoft.sqlserver.jdbc.SQLServerException: Database 'db2' does not exist. Make sure that the name is entered correctly.

Please let me know Can I create a JDBC sink connector which uses more than one databases for source topic.
If this scenario is possible then How can I achieve this by using JDBC Sink Connector?

Body:
I have used these properties and it worked for me in this case(if we have multiple source db and one target db to store that data)

table.name.format=iq_${topic}
transforms=dropPrefix
transforms.dropPrefix.replacement=$1_transferiq_$2
transforms.dropPrefix.regex=iq.(.*).transferiq.(.*)
transforms.dropPrefix.type=org.apache.kafka.connect.transforms.RegexRouter


问:
How to build/transform an object from a previous object
I'm trying to create an object in a certain structure from data that I have.
From:

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}
```
<p>to:</p>
<pre class="lang-js prettyprint-override"><code>const resultGender = {
    "data": [
        {
            "value": 30, "label": { "en": "male"}
        },
        {
            "value": 70, "label": { "en": "female"}
        }
    ]
}

const resultFruits = {
    "data": [
        {
            "value": 30, "label": { "en": "apple"}
        },
        {
            "value": 20, "label": { "en": "banana"}
        },
        {
            "value": 200, "label": { "en": "strawberry"}
        }
    ]
}
</code>```

In other words, a function that accepts dataGender and returns resultGender, and likewise for dataFruits.

As can be seen above, the structure is consistent, and the only changes are to the values under keys "value" and "en".

I found ramda's objOf() which brings me close:

const R = require("ramda")

const buildObject = R.compose(
  R.objOf("data"),
  R.map(R.objOf("value"))
);
buildObject([30, 70]); // => {"data": [{"value": 30}, {"value": 70}]}
```

But I'm still missing how to build the textual part under "en".

答:
Convert the object to pairs of [key, value], and map them to a new object using R.applySpec. Use R.nth to take the key or value from the pair. Use R.objOf to nest the array under data.

const { pipe, toPairs, map, applySpec, nth, objOf } = R

const fn = pipe(
  toPairs,
  map(applySpec({
    value: nth(1),
    label: {
      en: nth(0)
    }
  })),
  objOf('data')
)

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}

console.log(fn(dataGender))
console.log(fn(dataFruits))```
<pre class="snippet-code-css lang-css prettyprint-override"><code>.as-console-wrapper {max-height: 100% !important; top: 0}</code>```

```
</div>
</div>
</p>
<p>The main behavior is taking an object, converting it pairs, and then applying a spec to each pair. We can extract this to a function (<code>mapSpec</code>), and then build are specific function with it:</p>
<p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>const { curry, map, toPairs, applySpec, pipe, nth, objOf } = R

const mapSpec = curry((spec, obj) => map(
  applySpec(spec),
  toPairs(obj)
))

const fn = pipe(
  mapSpec({
    value: nth(1),
    label: {
      en: nth(0)
    }
  }),
  objOf('data')
)

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}

console.log(fn(dataGender))
console.log(fn(dataFruits))</code>```

.as-console-wrapper {max-height: 100% !important; top: 0}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script></code>```

Body:
I'm trying to create an object in a certain structure from data that I have.
From:

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}

to:

const resultGender = {
    "data": [
        {
            "value": 30, "label": { "en": "male"}
        },
        {
            "value": 70, "label": { "en": "female"}
        }
    ]
}

const resultFruits = {
    "data": [
        {
            "value": 30, "label": { "en": "apple"}
        },
        {
            "value": 20, "label": { "en": "banana"}
        },
        {
            "value": 200, "label": { "en": "strawberry"}
        }
    ]
}

In other words, a function that accepts dataGender and returns resultGender, and likewise for dataFruits.

As can be seen above, the structure is consistent, and the only changes are to the values under keys "value" and "en".

I found ramda's objOf() which brings me close:

const R = require("ramda")

const buildObject = R.compose(
  R.objOf("data"),
  R.map(R.objOf("value"))
);
buildObject([30, 70]); // => {"data": [{"value": 30}, {"value": 70}]}

But I'm still missing how to build the textual part under "en".

Body:
Convert the object to pairs of [key, value], and map them to a new object using R.applySpec. Use R.nth to take the key or value from the pair. Use R.objOf to nest the array under data.

const { pipe, toPairs, map, applySpec, nth, objOf } = R

const fn = pipe(
  toPairs,
  map(applySpec({
    value: nth(1),
    label: {
      en: nth(0)
    }
  })),
  objOf('data')
)

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}

console.log(fn(dataGender))
console.log(fn(dataFruits))

.as-console-wrapper {max-height: 100% !important; top: 0}

<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

The main behavior is taking an object, converting it pairs, and then applying a spec to each pair. We can extract this to a function (mapSpec), and then build are specific function with it:

const { curry, map, toPairs, applySpec, pipe, nth, objOf } = R

const mapSpec = curry((spec, obj) => map(
  applySpec(spec),
  toPairs(obj)
))

const fn = pipe(
  mapSpec({
    value: nth(1),
    label: {
      en: nth(0)
    }
  }),
  objOf('data')
)

// example #1
const dataGender = {"male": 30, "female": 70}

// example #2
const dataFruits = {"apple": 10, "banana": 20, "strawberry": 200}

console.log(fn(dataGender))
console.log(fn(dataFruits))

.as-console-wrapper {max-height: 100% !important; top: 0}

<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>


问:
AWS Amplify Duplicate Error: Duplicated files or mocks
I set up a new amplify, added auth, and a post confirmation lambda function to move user data into DynamoDB. When I run NPM start, I get this error:

Failed to construct transformer:  DuplicateError: Duplicated files or mocks. Please check the console for more info
at setModule (C:\Users\cjfew\Desktop\Fresh\MyDemo\node_modules\jest-haste-map\build\index.js:543:17)
.js:426:22 {

mockPath1: 'amplify#current-cloud-backend\function\FreshAuthPostConfirmation\src\package.json',

mockPath2: 'amplify\backend\function\FreshAuthPostConfirmation\src\package.json'
}
'''

Based on what I have read, #current-cloud-backend gets created by amplify, based on the files in the backend folder. It seems like that package.json is supposed to be there, but I am not sure why it is an error. I saw somewhere that I should just delete the subclass duplicate file, which I assumed to be the one in #current-cloud-backend, but amplify is going to keep producing this error every time I push to it, how do I avoid this from happening at all?

答:
There is a discussion about this error in this Amplify GitHub Issue. The file package.json appears twice to jest-haste-map, and the solution is to explicitly  ignore the #current-cloud-backend folder when building and starting your app.

The solution to the problem depends on your version of React Native: here you find an overview of how exlusion of files work for different versions. For example, you can create a metro.config.js file with the following contents to exclude the #current-cloud-backend:

```<code>const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = {
  resolver: {
    blacklistRE: exclusionList([/#current-cloud-backend\/.*/])
  }
};
</code>```

And install metro-config as a dev dependency. If that doesn't work there are some other solutions in the links that you can try out.

Body:
There is a discussion about this error in this Amplify GitHub Issue. The file package.json appears twice to jest-haste-map, and the solution is to explicitly  ignore the #current-cloud-backend folder when building and starting your app.

The solution to the problem depends on your version of React Native: here you find an overview of how exlusion of files work for different versions. For example, you can create a metro.config.js file with the following contents to exclude the #current-cloud-backend:

const exclusionList = require('metro-config/src/defaults/exclusionList');
module.exports = {
  resolver: {
    blacklistRE: exclusionList([/#current-cloud-backend\/.*/])
  }
};

And install metro-config as a dev dependency. If that doesn't work there are some other solutions in the links that you can try out.


问:
How to avoid HOME in the url an show only the view name?
Is there a way to avoid showing Home in the URL and only the name of the view?

I mean if I want to open the view About which is in the Home controller my URL will be like this:

www.website.com/Home/About

But I would love to show only

www.website.com/About

How can I do this?

答:
You should then create a controller called 'About'.

Create inside Controller About -> Action "Index".

Copy the content from Home/About to About/Index.

If you want to go to more complicated way and it is not highly recommended (according to this discussion ) please see this solution


问:
Sum of variabels from each for loop iterations
I want to solve this exercise https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/python

How can I count result of count variable from first and second loop together ?
Result from first loop is 4-2 = 2, now I want sum of the 2 + result from second loop 10-6= 4. So the total sum should be 6 . Thanks for your tips :)

```<code>array =[(4,2), (10,6)]
print((array))
print(array[1])

for x in array:
    count = (x[0] - x[1])
    print (count)
</code>```

I am not sure if this process is good for the exercise but I want it try this way :)

Thanks :)

答:
your approach doesn't look too bad. Iterating over the bus stop to find the difference of people leaving and joining the bus. You only have to add one variable that adds up these differences. I called it counter, which counts the current number of people on the bus. It starts with a value of zero. Then in each iteration, we add the difference to the counter variable. If more people leave the bus than enter it the counter will be decreased as current_difference is negative.
Here is an example implementation:

```<code>array =[(4,2), (10,6)]

counter = 0
for x in array:
    current_difference = (x[0] - x[1])
    print (current_difference)
    counter = counter + current_difference

print(counter)
</code>```

Result:

```<code>6
</code>```

Body:
I want to solve this exercise https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/python

How can I count result of count variable from first and second loop together ?
Result from first loop is 4-2 = 2, now I want sum of the 2 + result from second loop 10-6= 4. So the total sum should be 6 . Thanks for your tips :)

array =[(4,2), (10,6)]
print((array))
print(array[1])

for x in array:
    count = (x[0] - x[1])
    print (count)

I am not sure if this process is good for the exercise but I want it try this way :)

Thanks :)

Body:
your approach doesn't look too bad. Iterating over the bus stop to find the difference of people leaving and joining the bus. You only have to add one variable that adds up these differences. I called it counter, which counts the current number of people on the bus. It starts with a value of zero. Then in each iteration, we add the difference to the counter variable. If more people leave the bus than enter it the counter will be decreased as current_difference is negative.
Here is an example implementation:

array =[(4,2), (10,6)]

counter = 0
for x in array:
    current_difference = (x[0] - x[1])
    print (current_difference)
    counter = counter + current_difference

print(counter)

Result:

6


问:
How to format current time using a yyyyMMddHHmmss format?
I'm trying to format the current time using this format yyyyMMddHHmmss.

```<code>t := time.Now()
fmt.Println(t.Format("yyyyMMddHHmmss"))
</code>```

That is outputting:

```<code>yyyyMMddHHmmss
</code>```

Any suggestions?

答:
Go standard library: time

now := time.Now()
fmt.Println(now) // 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
fmt.Println(now.Format("20060102150405"))
fmt.Println(now.Format("2006/01/02/15/04/05"))
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("2006-01-02 15:04"))
fmt.Println(now.Format("2006/01/02 15:04:05"))
fmt.Println(now.Format("2006/01/02 15:04:05 (-0700)"))
fmt.Println(now.Format("2006年01月02日 15:04"))
fmt.Println(now.Format(time.Layout))   // 01/02 03:04:05PM '06 -0700
fmt.Println(now.Format(time.ANSIC))    // Mon Jan _2 15:04:05 2006
fmt.Println(now.Format(time.UnixDate)) // Mon Jan _2 15:04:05 MST 2006
fmt.Println(now.Format(time.RubyDate)) // Mon Jan 02 15:04:05 -0700 2006
fmt.Println(now.Format(time.RFC822))   // 02 Jan 06 15:04 MST
fmt.Println(now.Format(time.RFC850))   // Monday, 02-Jan-06 15:04:05 MST
fmt.Println(now.Format(time.Kitchen))  // 3:04PM
fmt.Println(now.Format(time.Stamp))    // Jan _2 15:04:05
```

Go playground

ContentLicense: CC BY-SA 3.0

Body:
I'm trying to format the current time using this format yyyyMMddHHmmss.

t := time.Now()
fmt.Println(t.Format("yyyyMMddHHmmss"))

That is outputting:

yyyyMMddHHmmss

Any suggestions?

Body:
Go standard library: time

now := time.Now()
fmt.Println(now) // 2009-11-10 23:00:00 +0000 UTC m=+0.000000001
fmt.Println(now.Format("20060102150405"))
fmt.Println(now.Format("2006/01/02/15/04/05"))
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("2006-01-02 15:04"))
fmt.Println(now.Format("2006/01/02 15:04:05"))
fmt.Println(now.Format("2006/01/02 15:04:05 (-0700)"))
fmt.Println(now.Format("2006年01月02日 15:04"))
fmt.Println(now.Format(time.Layout))   // 01/02 03:04:05PM '06 -0700
fmt.Println(now.Format(time.ANSIC))    // Mon Jan _2 15:04:05 2006
fmt.Println(now.Format(time.UnixDate)) // Mon Jan _2 15:04:05 MST 2006
fmt.Println(now.Format(time.RubyDate)) // Mon Jan 02 15:04:05 -0700 2006
fmt.Println(now.Format(time.RFC822))   // 02 Jan 06 15:04 MST
fmt.Println(now.Format(time.RFC850))   // Monday, 02-Jan-06 15:04:05 MST
fmt.Println(now.Format(time.Kitchen))  // 3:04PM
fmt.Println(now.Format(time.Stamp))    // Jan _2 15:04:05

Go playground


问:
Lightweight IDE for Linux
Even though I have a robust and fast computer (Pentium Dual Core 2.0 with 2Gb RAM), I'm always searching for lightweight software to have on it, so it runs fast even when many apps are up and running simultaneously.

On the last few weeks I've been migrating gradually to Linux and want to install a free lightweight yet useful IDE to program on C++ and PHP.  Syntax highlighting and code completion tips are must-haves.

答: I would say Bluefish, not an I.D.E but a nice lightweight code editor with syntax highlighting and code completion (and many others) for quite an array of languages (among them C and Php).

Title: Lightweight IDE for Linux


问:
Could managed code (specifically .NET) ever become 'unmanaged'?
Recently I was talking with a friend of mine who had started a C++ class a couple months ago (his first exposure to programming). We got onto the topic of C# and .NET generally, and he made the point to me that he felt it was 'doomed' for all of the commonly-cited issues (low speed, breakable bytecode, etc). I agreed with him on all those issues, but I held back in saying it was doomed, only because I felt that, in time, languages like C# could instead become native code (if Microsoft so chose to change the implementation of .NET from a bytecode, JIT runtime environemnent to one which compiles directly to native code like your C++ program does).

My question is, am I out to lunch here? I mean, it may take a lot of work (and may break too many things), but there isn't some type of magical barrier which prevents C# code from being compiled natively (if one wanted to do it), right? There was a time where C++ was considered a very high-level language (which it still is, but not as much as in the past) yet now it's the bedrock (along with C) for Microsoft's native APIs. The idea that .NET could one day be on the same level as C++ in that respect seems only to be a matter of time and effort to me, not some fundamental flaw in the design of the language.

EDIT: I should add that if native compilation of .NET is possible, why does Microsoft choose not to go that route? Why have they chosen the JIT bytecode path?

答: C# can be natively compiled using tool such as NGEN, and the MONO (open source .net framework) team has developed full AOT (ahead of time) compilation which allows c# to run on the IPhone. However, full compilation is culbersome because it destroys cross-platform compatibility, and some machine-specific optimizations cannot be done. However, it is also important to note that .net is not an interpreted language, but a JIT (just in time) compiled language, which means it runs natively on the machine.</p>

OwnerDisplayName: GRB

ContentLicense: CC BY-SA 2.5

LastEditorDisplayName: Joel Coehoorn

OwnerDisplayName: Razispio


问:
How to check for variable existence in codeigniter(php)? newb question
Hey I'm new to php and codeigniter.  I know that in codeigniter's view you can echo a variable like

```<code><?php echo $var ?>
</code>```

but if say, I don't pass the variable $var, I get a nasty

```<code><h4>A PHP Error was encountered</h4>
</code>```

in my html source code.  I've worked with django before an in their template, if the variable doesn't exist, they simply don't render it.  Is there a way in php/codeigniter to say 'if $var exists do smthing else do nothing' ?

I tried:

```<code><?php if($title): ?>
    <?php echo $title ?>
<?php endif; ?>
</code>```

but that was an error.  Thanks!

答:
Use the isset() function to test if a variable has been declared.

```<code>if (isset($var)) echo $var;
</code>```

Use the empty() function to test if a variable has no content such as NULL, "", false or 0.

Body:
Hey I'm new to php and codeigniter.  I know that in codeigniter's view you can echo a variable like

<?php echo $var ?>

but if say, I don't pass the variable $var, I get a nasty

A PHP Error was encountered

in my html source code.  I've worked with django before an in their template, if the variable doesn't exist, they simply don't render it.  Is there a way in php/codeigniter to say 'if $var exists do smthing else do nothing' ?

I tried:

<?php if($title): ?>
    <?php echo $title ?>
<?php endif; ?>

but that was an error.  Thanks!

Body:
Use the isset() function to test if a variable has been declared.

if (isset($var)) echo $var;

Use the empty() function to test if a variable has no content such as NULL, "", false or 0.


问:
repetitive input/output entity declaration vhdl
I've got multiple (same) input/output to declare :

```<code>B1_data_to_send         : in std_logic_vector(15 downto 0);
B1_start_transmission   : out std_logic;
B1_transmission_busy    : in std_logic;

B2_data_to_send         : in std_logic_vector(15 downto 0);
B2_start_transmission   : out std_logic;
B2_transmission_busy    : in std_logic;

B3_data_to_send         : in std_logic_vector(15 downto 0);
B3_start_transmission   : out std_logic;
B3_transmission_busy    : in std_logic;

B4_data_to_send         : in std_logic_vector(15 downto 0);
B4_start_transmission   : out std_logic;
B4_transmission_busy    : in std_logic;

B5_data_to_send         : in std_logic_vector(15 downto 0);
B5_start_transmission   : out std_logic;
B5_transmission_busy    : in std_logic;

B6_data_to_send         : in std_logic_vector(15 downto 0);
B6_start_transmission   : out std_logic;
B6_transmission_busy    : in std_logic;
</code>```

I've got 30 block like this to create, is there a way to avoid this repetition and to create a generic block that I can instantiate with different names ?

答:
Arrays are what you need here. First, you will need to create an array type of std_logic_vector in a package. If you're using VHDL 2008, it can simply be an unconstrained type:

package types_pkg is
  type slv_array_t is array(natural range <>) of std_logic_vector;
end package;
```
<p>and then use this type in your entity:</p>
<pre class="lang-vhdl prettyprint-override"><code>use work.types_pkg.all;

entity your_entity is
  port (
    B_data_to_send       : in  slv_array_t     (1 to 30)(15 downto 0);
    B_start_transmission : out std_logic_vector(1 to 30);
    B_transmission_busy  : in  std_logic_vector(1 to 30)
  );
end entity;
</code>```

Of course, any of the dimensions can come from a generic.

use work.types_pkg.all;

entity your_entity is
  generic (
    G_N_PORTS : natural;
    G_D_WIDTH : natural
  );
  port (
    B_data_to_send       : in  slv_array_t     (0 to G_N_PORTS-1)(G_D_WIDTH-1 downto 0);
    B_start_transmission : out std_logic_vector(0 to G_N_PORTS-1);
    B_transmission_busy  : in  std_logic_vector(0 to G_N_PORTS-1)
  );
end entity;
```

Body:
I've got multiple (same) input/output to declare :

B1_data_to_send         : in std_logic_vector(15 downto 0);
B1_start_transmission   : out std_logic;
B1_transmission_busy    : in std_logic;

B2_data_to_send         : in std_logic_vector(15 downto 0);
B2_start_transmission   : out std_logic;
B2_transmission_busy    : in std_logic;

B3_data_to_send         : in std_logic_vector(15 downto 0);
B3_start_transmission   : out std_logic;
B3_transmission_busy    : in std_logic;

B4_data_to_send         : in std_logic_vector(15 downto 0);
B4_start_transmission   : out std_logic;
B4_transmission_busy    : in std_logic;

B5_data_to_send         : in std_logic_vector(15 downto 0);
B5_start_transmission   : out std_logic;
B5_transmission_busy    : in std_logic;

B6_data_to_send         : in std_logic_vector(15 downto 0);
B6_start_transmission   : out std_logic;
B6_transmission_busy    : in std_logic;

I've got 30 block like this to create, is there a way to avoid this repetition and to create a generic block that I can instantiate with different names ?

Body:
Arrays are what you need here. First, you will need to create an array type of std_logic_vector in a package. If you're using VHDL 2008, it can simply be an unconstrained type:

package types_pkg is
  type slv_array_t is array(natural range <>) of std_logic_vector;
end package;

and then use this type in your entity:

use work.types_pkg.all;

entity your_entity is
  port (
    B_data_to_send       : in  slv_array_t     (1 to 30)(15 downto 0);
    B_start_transmission : out std_logic_vector(1 to 30);
    B_transmission_busy  : in  std_logic_vector(1 to 30)
  );
end entity;

Of course, any of the dimensions can come from a generic.

use work.types_pkg.all;

entity your_entity is
  generic (
    G_N_PORTS : natural;
    G_D_WIDTH : natural
  );
  port (
    B_data_to_send       : in  slv_array_t     (0 to G_N_PORTS-1)(G_D_WIDTH-1 downto 0);
    B_start_transmission : out std_logic_vector(0 to G_N_PORTS-1);
    B_transmission_busy  : in  std_logic_vector(0 to G_N_PORTS-1)
  );
end entity;