برنامه نویسی

میزبانی برنامه ها با AWS CDK: مقدمه ای بر زیرساخت به عنوان کد

Summarize this content to 400 words in Persian Lang

مقدمه

تاکنون زیرساخت برنامه هایم را به صورت دستی ایجاد کرده ام. این بار، خودم را به چالش کشیدم تا زیرساخت را با استفاده از Infrastructure as Code (IaC) مدیریت کنم.

با استفاده از AWS CDK، من توانستم تنظیمات زیرساخت پیچیده را مدون کنم، آنها را به سادگی مدیریت کنم و استقرار کارآمد را فعال کنم. در این مقاله نحوه میزبانی برنامه با استفاده از IaC با AWS CDK را معرفی می کنم.

مروری بر معماری اپلیکیشن

اپلیکیشنی که این بار ایجاد کردم ساختار زیر را دارد. من آن را به گونه ای طراحی کردم که با استفاده از تصاویر Docker در AWS هر دو قسمت ظاهری و باطن را به طور موثر اجرا کند.

Frontend: React + TypeScript
Backend: NestJS + TypeScript در زیر نموداری از معماری برنامه آمده است:

طراحی زیرساخت

ابتدا باید یک پیکربندی شبکه امن با استفاده از VPC (Virtual Private Cloud) ایجاد کنم. بنابراین، من زیرشبکه های عمومی و خصوصی را در VPC راه اندازی کردم.

من یک ALB (Application Load Balancer) را در زیرشبکه عمومی و سرورهای backend و frontend که توسط ECS (Elastic Container Service) Fargate مدیریت می شوند در زیر شبکه خصوصی قرار دادم. این تضمین می کند که سرورها مستقیماً در معرض خارج قرار نگیرند و امنیت را افزایش می دهد.

کاربران می توانند از طریق دروازه اینترنت به برنامه دسترسی داشته باشند. دسترسی از طریق ALB قرار داده شده در زیر شبکه عمومی دریافت می شود. ALB برای هدایت درخواست ها به خدمات مناسب و انجام تعادل بار طراحی شده است.

برای پایگاه داده، تصمیم گرفتم از DynamoDB برای ذخیره داده ها استفاده کنم. وظایف ECS در زیرشبکه خصوصی برای دسترسی به DynamoDB از طریق نقاط پایانی VPC پیکربندی شده‌اند. این امکان ارتباط با DynamoDB را بدون استفاده از اینترنت فراهم می کند و امنیت و عملکرد ارتباط را بهبود می بخشد.

تصاویر Docker استفاده شده توسط سرورها در ECR (Elastic Container Registry) ذخیره می شوند. ECS از این تصاویر برای اجرای برنامه استفاده می کند. علاوه بر این، با دریافت تصاویر از S3 و ECR از طریق نقاط انتهایی VPC، امکان استفاده از منابع در شبکه خصوصی وجود دارد.

پیاده سازی

بعد، طراحی زیرساخت فوق را به کد ترجمه می کنم.

راه اندازی

ابتدا اطلاعات احراز هویت را با استفاده از AWS CLI تنظیم کنید:

aws configure

سپس، راه اندازی CDK را ادامه دهید:

npm install -g aws-cdk
mkdir cdk && cd cdk # Create a directory of your choice and move into it
cdk init app –language typescript
cdk bootstrap aws://${AWS_ACCOUNT_ID}/${AWS_REGION_NAME}

این به طور خودکار فایل های لازم برای تنظیمات CDK را تولید می کند.

تصاویر Docker را برای هر دو قسمت جلویی و باطن آماده کنید و آنها را در ECR ذخیره کنید.

نوشتن محتوای CDK

اصلاح کنید cdk-stack.ts از آنجایی که کلاس CdkStack قبلا ایجاد شده است، طرح فوق را در سازنده آن بنویسید. من می خواهم محیط های مختلفی مانند آزمایش و تولید ایجاد کنم، بنابراین اضافه می کنم environment به عنوان یک استدلال برای سازنده برای استفاده در آینده.

export class CdkStack extends cdk.Stack {
constructor(
scope: Construct,
id: string,
environment: string, // Added
props?: cdk.StackProps
) {

این CdkStack کلاس در cdk.ts فراخوانی می شود، بنابراین کد را تغییر دهید cdk.ts برای قابل استفاده کردن محیط:

#!/usr/bin/env node
import * as cdk from “aws-cdk-lib”;
import “source-map-support/register”;
import { CdkStack } from “../lib/cdk-stack”;

const app = new cdk.App();
const environment = app.node.tryGetContext(“env”); // Obtain from command line during deployment
new CdkStack(app, `${environment}CdkStack`, environment);
app.synth();

از اینجا، محتوای داخل سازنده کلاس CdkStack را در cdk-stack.ts بنویسید.

ابتدا یک VPC ایجاد کنید. از آنجایی که این بار به زیرشبکه های خصوصی و عمومی نیاز داریم، به صورت زیر بنویسید:

const vpc = new ec2.Vpc(this, `${environment}AppVpc`, {
vpcName: `${environment}AppVpc`,
maxAzs: 2,
natGateways: 1,
subnetConfiguration: [
{
name: `${environment}AppPublicSubnet`,
subnetType: ec2.SubnetType.PUBLIC,
cidrMask: 24,
},
{
name: `${environment}AppPrivateSubnet`,
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
cidrMask: 24,
},
],
});

در مورد دروازه اینترنت، از آنجایی که ما در حال ایجاد یک زیر شبکه عمومی هستیم، به طور خودکار ایجاد می شود حتی اگر به صراحت در کد ذکر نشده باشد.

سپس، نقاط پایانی VPC را ایجاد کنید که زیرشبکه خصوصی از آنها استفاده خواهد کرد:

// VPC Endpoint for DynamoDB
vpc.addGatewayEndpoint(`${environment}DynamoDbEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.DYNAMODB,
});

// VPC Endpoint for S3 (necessary for obtaining Docker images)
vpc.addGatewayEndpoint(`${environment}S3Endpoint`, {
service: ec2.GatewayVpcEndpointAwsService.S3,
});

// VPC Endpoint for ECR (endpoint to send requests)
vpc.addInterfaceEndpoint(`${environment}EcrApiEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR,
subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

// VPC Endpoint for ECR Docker (endpoint to obtain Docker images)
vpc.addInterfaceEndpoint(`${environment}EcrDockerEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

// VPC Endpoint for CloudWatch Logs (for logging)
vpc.addInterfaceEndpoint(`${environment}CloudWatchLogsEndpoint`, {
service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS,
subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

همه اتصالات با زیر شبکه خصوصی هستند، اما برای DynamoDB و S3، می توانیم از a استفاده کنیمddGatewayEndpoint (gateway-type endpoints)، بنابراین تعیین زیر شبکه غیر ضروری است.

بعد، ECS را ایجاد کنید، که برای مدیریت کانتینر لازم است:

const cluster = new ecs.Cluster(this, `${environment}AppCluster`, {
clusterName: `${environment}AppCluster`,
vpc: vpc,
});

یک نقش IAM برای استفاده Fargate ایجاد کنید:

const taskExecutionRole = new iam.Role(
this,
`${environment}TaskExecutionRole`,
{
// Ensure only ECS tasks can assume this role
assumedBy: new iam.ServicePrincipal(“ecs-tasks.amazonaws.com”),
}
);

// Attach necessary access permissions
taskExecutionRole.addManagedPolicy(
iam.ManagedPolicy.fromAwsManagedPolicyName(
“service-role/AmazonECSTaskExecutionRolePolicy”
)
);

یک تعریف وظیفه Fargate برای frontend ایجاد کنید:

const frontendTaskDef = new ecs.FargateTaskDefinition(
this,
`${environment}AppFrontendTaskDef`,
{
family: `${environment}AppFrontendTaskDef`,
memoryLimitMiB: 512,
cpu: 256,
executionRole: taskExecutionRole,
}
);

از آنجایی که ما از ECR استفاده می کنیم، مخزن تصویر را برای frontend مشخص کنید (در صورت استفاده از Docker Hub غیر ضروری است):

const frontendRepository = ecr.Repository.fromRepositoryName(
this,
`${environment}frontendRepository`,
“app-react-nginx-image”
);

اطلاعات ظرف را به تعریف کار ایجاد شده در بالا اضافه کنید. این بار مخزن تصویر را مشخص می کنیم:

const frontendContainer = frontendTaskDef.addContainer(
`${environment}AppFrontendContainer`,
{
containerName: `${environment}AppFrontendContainer`,
image: ecs.ContainerImage.fromEcrRepository(
frontendRepository,
“latest”
),
logging: new ecs.AwsLogDriver({
streamPrefix: “Frontend”, // Prefix for logging
}),
}
);

frontendContainer.addPortMappings({
containerPort: 80,
protocol: ecs.Protocol.TCP,
});

را containerPort به پورت دسترسی از ALB به Fargate اشاره دارد، بنابراین بدون توجه به اینکه خود برنامه با HTTPS میزبانی شده باشد، می تواند 80 باقی بماند.

بعد، یک سرویس Fargate را برای سرور frontend راه اندازی کنید:

const frontendService = new ecs.FargateService(
this,
`${environment}AppFrontendService`,
{
serviceName: `${environment}AppFrontendService`,
cluster: cluster,
taskDefinition: frontendTaskDef,
assignPublicIp: false,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
}
);

// Configure auto-scaling
const scalingFrontend = frontendService.autoScaleTaskCount({
minCapacity: 1,
maxCapacity: 5,
});

// Auto-scale when CPU utilization exceeds 50%
scalingFrontend.scaleOnCpuUtilization(“CpuScalingFrontend”, {
targetUtilizationPercent: 50,
});

// Auto-scale when memory utilization exceeds 70%
scalingFrontend.scaleOnMemoryUtilization(“MemoryScalingFrontend”, {
targetUtilizationPercent: 70,
});

با این کار تنظیمات سرور frontend کامل می شود. در مرحله بعد، به راه اندازی سرور باطن ادامه دهید. از آنجایی که تقریباً مشابه سرور frontend است، توضیحات مختصر خواهد بود.

const backendTaskDef = new ecs.FargateTaskDefinition(
this,
`${environment}AppBackendTaskDef`,
{
family: `${environment}AppBackendTaskDef`,
memoryLimitMiB: 512,
cpu: 256,
executionRole: taskExecutionRole,
}
);

const backendRepository = ecr.Repository.fromRepositoryName(
this,
`${environment}backendRepository`,
“app-nestjs-image”
);

const backendContainer = backendTaskDef.addContainer(
`${environment}AppBackendContainer`,
{
containerName: `${environment}AppBackendContainer`,
image: ecs.ContainerImage.fromEcrRepository(
backendRepository,
“latest”
),
logging: new ecs.AwsLogDriver({
streamPrefix: “Backend”,
}),
}
);

backendContainer.addPortMappings({
containerPort: 3000,
protocol: ecs.Protocol.TCP,
});

const backendService = new ecs.FargateService(
this,
`${environment}AppBackendService`,
{
serviceName: `${environment}AppBackendService`,
cluster: cluster,
taskDefinition: backendTaskDef,
assignPublicIp: false,
vpcSubnets: {
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
}
);

const scalingBackend = backendService.autoScaleTaskCount({
minCapacity: 1,
maxCapacity: 5,
});

scalingBackend.scaleOnCpuUtilization(“CpuScalingBackend”, {
targetUtilizationPercent: 60,
});

scalingBackend.scaleOnMemoryUtilization(“MemoryScalingBackend”, {
targetUtilizationPercent: 75,
});

بعد، ALB را ایجاد کنید:

const loadBalancer = new elbv2.ApplicationLoadBalancer(
this,
`${environment}AppApplicationLoadBalancer`,
{
vpc,
loadBalancerName: `${environment}AppApplicationLoadBalancer`,
internetFacing: true,
}
);

ALB را به منابع موجود در زیرشبکه خصوصی وصل کنید:

const FrontendTargetGroup = new elbv2.ApplicationTargetGroup(
this,
`${environment}AppFrontendTG`,
{
targetGroupName: `${environment}AppFrontendTG`,
vpc,
port: 80,
protocol: elbv2.ApplicationProtocol.HTTP,
targets: [frontendService],
healthCheck: {
path: “/”,
},
}
);

const backendTargetGroup = new elbv2.ApplicationTargetGroup(
this,
`${environment}AppBackendTG`,
{
targetGroupName: `${environment}AppBackendTG`,
vpc,
port: 3000,
protocol: elbv2.ApplicationProtocol.HTTP,
targets: [backendService],
healthCheck: {
path: “/v1/health”, // Specify the API for health checks
},
}
);

رسیدگی به درخواست ALB را شرح دهید:

const listener = loadBalancer.addListener(`${environment}HttpListener`, {
port: 80,
open: true,
defaultAction: elbv2.ListenerAction.forward([FrontendTargetGroup]),
});

listener.addTargetGroups(`${environment}BackendTargetGroups`, {
targetGroups: [backendTargetGroup],
priority: 1,
conditions: [elbv2.ListenerCondition.pathPatterns([“/v1/*”])],
});

این بار، ما از یک ALB استفاده می کنیم تا ترافیک را در قسمت جلو و باطن توزیع کنیم. به‌طور پیش‌فرض، درخواست‌ها به فرانت‌اند ارسال می‌شوند، اما اگر دسترسی با v1/ وجود داشته باشد، به‌صورت پیش‌فرض به باطن Fargate ارسال می‌شود. اگر از دو ALB استفاده می کنید، پیکربندی addTargetGroups ضروری نیست.

در نهایت جدول DynamoDB لازم را ایجاد کنید. در DynamoDB، فقط PartitionKey اجباری است، اما می‌توانید SortKey، Index و TTL را در صورت نیاز اضافه کنید.

const usersTable = new dynamodb.Table(this, `${environment}UsersTable`, {
tableName: `${environment}-users`,
partitionKey: {
name: “account_id”,
type: dynamodb.AttributeType.STRING,
},
});
usersTable.grantReadWriteData(backendTaskDef.taskRole);

اکنون که کد لازم برای IaC را نوشته ایم، فقط باید آن را اجرا و تأیید کنیم.

اعدام

ابتدا خطاهای نحوی در کد CDK بالا را بررسی کنید:

cdk synth –context env=dev

در صورت وجود خطا، آنها به صورت زیر ظاهر می شوند، بنابراین در صورت نیاز آنها را برطرف کنید:

Error: Validation failed with the following errors:
[undefinedCdkStack/undefinedAppFrontendTargetGroup] Target group name: “undefinedAppFrontendTargetGroup” can have a maximum of 32 characters.
[undefinedCdkStack/undefinedAppBackendTargetGroup] Target group name: “undefinedAppBackendTargetGroup” can have a maximum of 32 characters.

اگر می توانید بدون خطا اجرا کنید، به استقرار ادامه دهید. اگر تصویر Docker به درستی کار نکند، Fargate بارها راه اندازی می شود، با خطا مواجه می شود، خاموش می شود و مجددا راه اندازی می شود. اگر زمان زیادی می برد، لطفاً کنسول مدیریت AWS را بررسی کنید.

cdk deploy –context env=dev

با این کار، میزبانی برنامه با استفاده از IaC کامل شده است.

نتیجه گیری

این اولین تلاش من در IaC بود، و با مدیریت زیرساخت به عنوان کد، ساخت زیرساخت برای محیط‌های مختلف مانند تولید و توسعه بدون خطای انسانی بسیار مفید بود.

اگر این مقاله به هر نحوی مفید بود، خوشحال می شوم اگر بتوانید دکمه لایک را فشار دهید! ممنون که تا آخر خواندید.

مقدمه

تاکنون زیرساخت برنامه هایم را به صورت دستی ایجاد کرده ام. این بار، خودم را به چالش کشیدم تا زیرساخت را با استفاده از Infrastructure as Code (IaC) مدیریت کنم.

با استفاده از AWS CDK، من توانستم تنظیمات زیرساخت پیچیده را مدون کنم، آنها را به سادگی مدیریت کنم و استقرار کارآمد را فعال کنم. در این مقاله نحوه میزبانی برنامه با استفاده از IaC با AWS CDK را معرفی می کنم.

مروری بر معماری اپلیکیشن

اپلیکیشنی که این بار ایجاد کردم ساختار زیر را دارد. من آن را به گونه ای طراحی کردم که با استفاده از تصاویر Docker در AWS هر دو قسمت ظاهری و باطن را به طور موثر اجرا کند.

  • Frontend: React + TypeScript
  • Backend: NestJS + TypeScript در زیر نموداری از معماری برنامه آمده است:

نموداری از معماری برنامه

طراحی زیرساخت

ابتدا باید یک پیکربندی شبکه امن با استفاده از VPC (Virtual Private Cloud) ایجاد کنم. بنابراین، من زیرشبکه های عمومی و خصوصی را در VPC راه اندازی کردم.

من یک ALB (Application Load Balancer) را در زیرشبکه عمومی و سرورهای backend و frontend که توسط ECS (Elastic Container Service) Fargate مدیریت می شوند در زیر شبکه خصوصی قرار دادم. این تضمین می کند که سرورها مستقیماً در معرض خارج قرار نگیرند و امنیت را افزایش می دهد.

کاربران می توانند از طریق دروازه اینترنت به برنامه دسترسی داشته باشند. دسترسی از طریق ALB قرار داده شده در زیر شبکه عمومی دریافت می شود. ALB برای هدایت درخواست ها به خدمات مناسب و انجام تعادل بار طراحی شده است.

برای پایگاه داده، تصمیم گرفتم از DynamoDB برای ذخیره داده ها استفاده کنم. وظایف ECS در زیرشبکه خصوصی برای دسترسی به DynamoDB از طریق نقاط پایانی VPC پیکربندی شده‌اند. این امکان ارتباط با DynamoDB را بدون استفاده از اینترنت فراهم می کند و امنیت و عملکرد ارتباط را بهبود می بخشد.

تصاویر Docker استفاده شده توسط سرورها در ECR (Elastic Container Registry) ذخیره می شوند. ECS از این تصاویر برای اجرای برنامه استفاده می کند. علاوه بر این، با دریافت تصاویر از S3 و ECR از طریق نقاط انتهایی VPC، امکان استفاده از منابع در شبکه خصوصی وجود دارد.

پیاده سازی

بعد، طراحی زیرساخت فوق را به کد ترجمه می کنم.

راه اندازی

ابتدا اطلاعات احراز هویت را با استفاده از AWS CLI تنظیم کنید:

aws configure

سپس، راه اندازی CDK را ادامه دهید:

npm install -g aws-cdk
mkdir cdk && cd cdk  # Create a directory of your choice and move into it
cdk init app --language typescript
cdk bootstrap aws://${AWS_ACCOUNT_ID}/${AWS_REGION_NAME}

این به طور خودکار فایل های لازم برای تنظیمات CDK را تولید می کند.

تصاویر Docker را برای هر دو قسمت جلویی و باطن آماده کنید و آنها را در ECR ذخیره کنید.

نوشتن محتوای CDK

اصلاح کنید cdk-stack.ts از آنجایی که کلاس CdkStack قبلا ایجاد شده است، طرح فوق را در سازنده آن بنویسید. من می خواهم محیط های مختلفی مانند آزمایش و تولید ایجاد کنم، بنابراین اضافه می کنم environment به عنوان یک استدلال برای سازنده برای استفاده در آینده.

export class CdkStack extends cdk.Stack {
  constructor(
    scope: Construct,
    id: string,
    environment: string, // Added
    props?: cdk.StackProps
  ) {

این CdkStack کلاس در cdk.ts فراخوانی می شود، بنابراین کد را تغییر دهید cdk.ts برای قابل استفاده کردن محیط:

#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";
import "source-map-support/register";
import { CdkStack } from "../lib/cdk-stack";

const app = new cdk.App();
const environment = app.node.tryGetContext("env"); // Obtain from command line during deployment
new CdkStack(app, `${environment}CdkStack`, environment);
app.synth();

از اینجا، محتوای داخل سازنده کلاس CdkStack را در cdk-stack.ts بنویسید.

ابتدا یک VPC ایجاد کنید. از آنجایی که این بار به زیرشبکه های خصوصی و عمومی نیاز داریم، به صورت زیر بنویسید:

const vpc = new ec2.Vpc(this, `${environment}AppVpc`, {
  vpcName: `${environment}AppVpc`,
  maxAzs: 2,
  natGateways: 1,
  subnetConfiguration: [
    {
      name: `${environment}AppPublicSubnet`,
      subnetType: ec2.SubnetType.PUBLIC,
      cidrMask: 24,
    },
    {
      name: `${environment}AppPrivateSubnet`,
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
      cidrMask: 24,
    },
  ],
});

در مورد دروازه اینترنت، از آنجایی که ما در حال ایجاد یک زیر شبکه عمومی هستیم، به طور خودکار ایجاد می شود حتی اگر به صراحت در کد ذکر نشده باشد.

سپس، نقاط پایانی VPC را ایجاد کنید که زیرشبکه خصوصی از آنها استفاده خواهد کرد:

// VPC Endpoint for DynamoDB
vpc.addGatewayEndpoint(`${environment}DynamoDbEndpoint`, {
  service: ec2.InterfaceVpcEndpointAwsService.DYNAMODB,
});

// VPC Endpoint for S3 (necessary for obtaining Docker images)
vpc.addGatewayEndpoint(`${environment}S3Endpoint`, {
  service: ec2.GatewayVpcEndpointAwsService.S3,
});

// VPC Endpoint for ECR (endpoint to send requests)
vpc.addInterfaceEndpoint(`${environment}EcrApiEndpoint`, {
  service: ec2.InterfaceVpcEndpointAwsService.ECR,
  subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

// VPC Endpoint for ECR Docker (endpoint to obtain Docker images)
vpc.addInterfaceEndpoint(`${environment}EcrDockerEndpoint`, {
  service: ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
  subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

// VPC Endpoint for CloudWatch Logs (for logging)
vpc.addInterfaceEndpoint(`${environment}CloudWatchLogsEndpoint`, {
  service: ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS,
  subnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
});

همه اتصالات با زیر شبکه خصوصی هستند، اما برای DynamoDB و S3، می توانیم از a استفاده کنیمddGatewayEndpoint (gateway-type endpoints)، بنابراین تعیین زیر شبکه غیر ضروری است.

بعد، ECS را ایجاد کنید، که برای مدیریت کانتینر لازم است:

const cluster = new ecs.Cluster(this, `${environment}AppCluster`, {
  clusterName: `${environment}AppCluster`,
  vpc: vpc,
});

یک نقش IAM برای استفاده Fargate ایجاد کنید:

const taskExecutionRole = new iam.Role(
  this,
  `${environment}TaskExecutionRole`,
  {
    // Ensure only ECS tasks can assume this role
    assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
  }
);

// Attach necessary access permissions
taskExecutionRole.addManagedPolicy(
  iam.ManagedPolicy.fromAwsManagedPolicyName(
    "service-role/AmazonECSTaskExecutionRolePolicy"
  )
);

یک تعریف وظیفه Fargate برای frontend ایجاد کنید:

const frontendTaskDef = new ecs.FargateTaskDefinition(
  this,
  `${environment}AppFrontendTaskDef`,
  {
    family: `${environment}AppFrontendTaskDef`,
    memoryLimitMiB: 512,
    cpu: 256,
    executionRole: taskExecutionRole,
  }
);

از آنجایی که ما از ECR استفاده می کنیم، مخزن تصویر را برای frontend مشخص کنید (در صورت استفاده از Docker Hub غیر ضروری است):

const frontendRepository = ecr.Repository.fromRepositoryName(
  this,
  `${environment}frontendRepository`,
  "app-react-nginx-image"
);

اطلاعات ظرف را به تعریف کار ایجاد شده در بالا اضافه کنید. این بار مخزن تصویر را مشخص می کنیم:

const frontendContainer = frontendTaskDef.addContainer(
  `${environment}AppFrontendContainer`,
  {
    containerName: `${environment}AppFrontendContainer`,
    image: ecs.ContainerImage.fromEcrRepository(
      frontendRepository,
      "latest"
    ),
    logging: new ecs.AwsLogDriver({
      streamPrefix: "Frontend", // Prefix for logging
    }),
  }
);

frontendContainer.addPortMappings({
  containerPort: 80,
  protocol: ecs.Protocol.TCP,
});

را containerPort به پورت دسترسی از ALB به Fargate اشاره دارد، بنابراین بدون توجه به اینکه خود برنامه با HTTPS میزبانی شده باشد، می تواند 80 باقی بماند.

بعد، یک سرویس Fargate را برای سرور frontend راه اندازی کنید:

const frontendService = new ecs.FargateService(
  this,
  `${environment}AppFrontendService`,
  {
    serviceName: `${environment}AppFrontendService`,
    cluster: cluster,
    taskDefinition: frontendTaskDef,
    assignPublicIp: false,
    vpcSubnets: {
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
  }
);

// Configure auto-scaling
const scalingFrontend = frontendService.autoScaleTaskCount({
  minCapacity: 1,
  maxCapacity: 5,
});

// Auto-scale when CPU utilization exceeds 50%
scalingFrontend.scaleOnCpuUtilization("CpuScalingFrontend", {
  targetUtilizationPercent: 50,
});

// Auto-scale when memory utilization exceeds 70%
scalingFrontend.scaleOnMemoryUtilization("MemoryScalingFrontend", {
  targetUtilizationPercent: 70,
});

با این کار تنظیمات سرور frontend کامل می شود. در مرحله بعد، به راه اندازی سرور باطن ادامه دهید. از آنجایی که تقریباً مشابه سرور frontend است، توضیحات مختصر خواهد بود.

const backendTaskDef = new ecs.FargateTaskDefinition(
  this,
  `${environment}AppBackendTaskDef`,
  {
    family: `${environment}AppBackendTaskDef`,
    memoryLimitMiB: 512,
    cpu: 256,
    executionRole: taskExecutionRole,
  }
);

const backendRepository = ecr.Repository.fromRepositoryName(
  this,
  `${environment}backendRepository`,
  "app-nestjs-image"
);

const backendContainer = backendTaskDef.addContainer(
  `${environment}AppBackendContainer`,
  {
    containerName: `${environment}AppBackendContainer`,
    image: ecs.ContainerImage.fromEcrRepository(
      backendRepository,
      "latest"
    ),
    logging: new ecs.AwsLogDriver({
      streamPrefix: "Backend",
    }),
  }
);

backendContainer.addPortMappings({
  containerPort: 3000,
  protocol: ecs.Protocol.TCP,
});

const backendService = new ecs.FargateService(
  this,
  `${environment}AppBackendService`,
  {
    serviceName: `${environment}AppBackendService`,
    cluster: cluster,
    taskDefinition: backendTaskDef,
    assignPublicIp: false,
    vpcSubnets: {
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
  }
);

const scalingBackend = backendService.autoScaleTaskCount({
  minCapacity: 1,
  maxCapacity: 5,
});

scalingBackend.scaleOnCpuUtilization("CpuScalingBackend", {
  targetUtilizationPercent: 60,
});

scalingBackend.scaleOnMemoryUtilization("MemoryScalingBackend", {
  targetUtilizationPercent: 75,
});

بعد، ALB را ایجاد کنید:

const loadBalancer = new elbv2.ApplicationLoadBalancer(
  this,
  `${environment}AppApplicationLoadBalancer`,
  {
    vpc,
    loadBalancerName: `${environment}AppApplicationLoadBalancer`,
    internetFacing: true,
  }
);

ALB را به منابع موجود در زیرشبکه خصوصی وصل کنید:

const FrontendTargetGroup = new elbv2.ApplicationTargetGroup(
  this,
  `${environment}AppFrontendTG`,
  {
    targetGroupName: `${environment}AppFrontendTG`,
    vpc,
    port: 80,
    protocol: elbv2.ApplicationProtocol.HTTP,
    targets: [frontendService],
    healthCheck: {
      path: "/",
    },
  }
);

const backendTargetGroup = new elbv2.ApplicationTargetGroup(
  this,
  `${environment}AppBackendTG`,
  {
    targetGroupName: `${environment}AppBackendTG`,
    vpc,
    port: 3000,
    protocol: elbv2.ApplicationProtocol.HTTP,
    targets: [backendService],
    healthCheck: {
      path: "/v1/health", // Specify the API for health checks
    },
  }
);

رسیدگی به درخواست ALB را شرح دهید:

const listener = loadBalancer.addListener(`${environment}HttpListener`, {
  port: 80,
  open: true,
  defaultAction: elbv2.ListenerAction.forward([FrontendTargetGroup]),
});

listener.addTargetGroups(`${environment}BackendTargetGroups`, {
  targetGroups: [backendTargetGroup],
  priority: 1,
  conditions: [elbv2.ListenerCondition.pathPatterns(["/v1/*"])],
});

این بار، ما از یک ALB استفاده می کنیم تا ترافیک را در قسمت جلو و باطن توزیع کنیم. به‌طور پیش‌فرض، درخواست‌ها به فرانت‌اند ارسال می‌شوند، اما اگر دسترسی با v1/ وجود داشته باشد، به‌صورت پیش‌فرض به باطن Fargate ارسال می‌شود. اگر از دو ALB استفاده می کنید، پیکربندی addTargetGroups ضروری نیست.

در نهایت جدول DynamoDB لازم را ایجاد کنید. در DynamoDB، فقط PartitionKey اجباری است، اما می‌توانید SortKey، Index و TTL را در صورت نیاز اضافه کنید.

const usersTable = new dynamodb.Table(this, `${environment}UsersTable`, {
  tableName: `${environment}-users`,
  partitionKey: {
    name: "account_id",
    type: dynamodb.AttributeType.STRING,
  },
});
usersTable.grantReadWriteData(backendTaskDef.taskRole);

اکنون که کد لازم برای IaC را نوشته ایم، فقط باید آن را اجرا و تأیید کنیم.

اعدام

ابتدا خطاهای نحوی در کد CDK بالا را بررسی کنید:

cdk synth --context env=dev

در صورت وجود خطا، آنها به صورت زیر ظاهر می شوند، بنابراین در صورت نیاز آنها را برطرف کنید:

Error: Validation failed with the following errors:
  [undefinedCdkStack/undefinedAppFrontendTargetGroup] Target group name: "undefinedAppFrontendTargetGroup" can have a maximum of 32 characters.
  [undefinedCdkStack/undefinedAppBackendTargetGroup] Target group name: "undefinedAppBackendTargetGroup" can have a maximum of 32 characters.

اگر می توانید بدون خطا اجرا کنید، به استقرار ادامه دهید. اگر تصویر Docker به درستی کار نکند، Fargate بارها راه اندازی می شود، با خطا مواجه می شود، خاموش می شود و مجددا راه اندازی می شود. اگر زمان زیادی می برد، لطفاً کنسول مدیریت AWS را بررسی کنید.

cdk deploy --context env=dev

با این کار، میزبانی برنامه با استفاده از IaC کامل شده است.

نتیجه گیری

این اولین تلاش من در IaC بود، و با مدیریت زیرساخت به عنوان کد، ساخت زیرساخت برای محیط‌های مختلف مانند تولید و توسعه بدون خطای انسانی بسیار مفید بود.

اگر این مقاله به هر نحوی مفید بود، خوشحال می شوم اگر بتوانید دکمه لایک را فشار دهید! ممنون که تا آخر خواندید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا