Skip to content

Commit

Permalink
Merge pull request #1396 from aligent/feature/DO-1704-nginx-for-mesh
Browse files Browse the repository at this point in the history
DO-1704: add nginx to the mesh
  • Loading branch information
TheOrangePuff authored Sep 10, 2024
2 parents 1c90151 + 214d5fa commit 4d77dc8
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 40 deletions.
3 changes: 3 additions & 0 deletions packages/graphql-mesh-server/assets/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM nginx:stable-alpine

COPY nginx.conf /etc/nginx/nginx.conf
53 changes: 53 additions & 0 deletions packages/graphql-mesh-server/assets/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
events {
worker_connections 1024;
}

http {
log_format json_combined escape=json
'{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status": "$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent"'
'}';

gzip on;
gzip_proxied any;
gzip_types text/plain application/json;
gzip_min_length 1000;

server {
listen 80;
access_log /var/log/nginx/access.log json_combined;

location /graphql {
# Reject requests with unsupported HTTP method
if ($request_method !~ ^(GET|POST|HEAD|OPTIONS|PUT|DELETE)$) {
return 405;
}

# Only requests matching the expectations will
# get sent to the application server
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache_bypass $http_upgrade;
}

location /healthcheck {
return 200;
}

location / {
return 403;
}
}
}
121 changes: 88 additions & 33 deletions packages/graphql-mesh-server/lib/fargate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { CfnIPSet, CfnWebACL } from "aws-cdk-lib/aws-wafv2";
import { ScalingInterval, AdjustmentType } from "aws-cdk-lib/aws-autoscaling";
import { ApplicationLoadBalancer } from "aws-cdk-lib/aws-elasticloadbalancingv2";
import { LogGroup } from "aws-cdk-lib/aws-logs";
import path = require("path");

export interface MeshServiceProps {
/**
Expand Down Expand Up @@ -182,6 +183,25 @@ export interface MeshServiceProps {
* @default - AWS generated task definition family name
*/
taskDefinitionFamilyName?: string;

/**
* Nginx image to use
*
* @default ecs.ContainerImage.fromRegistry("nginx:stable-alpine")
*/
nginxImage?: ecs.ContainerImage;

/**
* Disable the nginx sidecar container
*
* @default - false
*/
disableNginx?: boolean;

/**
* Optional manual overrides for nginx sidecar container
*/
nginxConfigOverride?: Partial<ecs.ContainerDefinitionOptions>;
}

export class MeshService extends Construct {
Expand All @@ -203,6 +223,8 @@ export class MeshService extends Construct {
)
: undefined;

if (!certificate) throw Error("Must pass certificate");

this.vpc =
props.vpc ||
new Vpc(this, "vpc", {
Expand Down Expand Up @@ -294,6 +316,65 @@ export class MeshService extends Construct {
logGroup: this.logGroup,
});

const taskDefinition = new ecs.FargateTaskDefinition(this, "TaskDef", {
memoryLimitMiB: props.memory || 1024,
cpu: props.cpu || 512,
});

// Configure nginx
if (!props.disableNginx) {
taskDefinition.addContainer("nginx", {
image:
props.nginxImage ||
ecs.ContainerImage.fromAsset(
path.resolve(__dirname, "../assets/nginx")
),
containerName: "nginx",
essential: true,
healthCheck: {
command: [
"CMD-SHELL",
"curl -f http://localhost || echo 'Health check failed'",
],
startPeriod: Duration.seconds(5),
},
logging: logDriver,
portMappings: [{ containerPort: 80 }],
...props.nginxConfigOverride,
});
}

// Add the main mesh container
taskDefinition.addContainer("mesh", {
image: ecs.ContainerImage.fromEcrRepository(this.repository),
containerName: "mesh",
environment: environment,
secrets: props.secrets ? props.secrets : ssmSecrets,
healthCheck: {
command: [
"CMD-SHELL",
"curl -f http://localhost || echo 'Health check failed'",
],
startPeriod: Duration.seconds(5),
},
logging: logDriver,
portMappings: [{ containerPort: 4000 }], // Main application listens on port 4000
});

// Configure x-ray
taskDefinition.addContainer("xray", {
image: ecs.ContainerImage.fromRegistry("amazon/aws-xray-daemon"),
cpu: 32,
containerName: "xray",
memoryReservationMiB: 256,
essential: false,
healthCheck: {
command: ["CMD-SHELL", "pgrep xray || echo 'Health check failed'"],
startPeriod: Duration.seconds(5),
},
portMappings: [{ containerPort: 4000, protocol: ecs.Protocol.UDP }],
});

// Create a load-balanced Fargate service and make it public
const fargateService =
new ecsPatterns.ApplicationLoadBalancedFargateService(this, `fargate`, {
Expand All @@ -304,22 +385,8 @@ export class MeshService extends Construct {
enableExecuteCommand: true,
cpu: props.cpu || 512, // 0.5 vCPU
memoryLimitMiB: props.memory || 1024, // 1 GB
taskImageOptions: {
image: ecs.ContainerImage.fromEcrRepository(this.repository),
enableLogging: true, // default
containerPort: 4000, // graphql mesh gateway port
secrets: props.secrets ? props.secrets : ssmSecrets, // Prefer v2 secrets using secrets manager
environment: environment,
logDriver: logDriver,
taskRole: new iam.Role(this, "MeshTaskRole", {
assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
}),
family:
props.taskDefinitionFamilyName !== undefined
? props.taskDefinitionFamilyName
: undefined,
},
publicLoadBalancer: true, // default,
taskDefinition: taskDefinition,
publicLoadBalancer: true, // defult,
taskSubnets: {
subnets: [...this.vpc.privateSubnets],
},
Expand All @@ -329,23 +396,11 @@ export class MeshService extends Construct {
this.service = fargateService.service;
this.loadBalancer = fargateService.loadBalancer;

// Configure x-ray
const xray = this.service.taskDefinition.addContainer("xray", {
image: ecs.ContainerImage.fromRegistry("amazon/aws-xray-daemon"),
cpu: 32,
memoryReservationMiB: 256,
essential: false,
});
xray.addPortMappings({
containerPort: 2000,
protocol: ecs.Protocol.UDP,
});

this.service.taskDefinition.taskRole.addManagedPolicy({
taskDefinition.taskRole.addManagedPolicy({
managedPolicyArn:
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
});
this.service.taskDefinition.taskRole.addManagedPolicy({
taskDefinition.taskRole.addManagedPolicy({
managedPolicyArn: "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess",
});

Expand Down Expand Up @@ -539,15 +594,15 @@ export class MeshService extends Construct {

this.firewall.addAssociation(
"loadbalancer-association",
fargateService.loadBalancer.loadBalancerArn
this.loadBalancer.loadBalancerArn
);

fargateService.targetGroup.configureHealthCheck({
path: "/healthcheck",
});

// Setup auto scaling policy
const scaling = fargateService.service.autoScaleTaskCount({
const scaling = this.service.autoScaleTaskCount({
minCapacity: props.minCapacity || 1,
maxCapacity: props.maxCapacity || 5,
});
Expand All @@ -558,7 +613,7 @@ export class MeshService extends Construct {
{ lower: 85, change: +3 },
];

const cpuUtilization = fargateService.service.metricCpuUtilization();
const cpuUtilization = this.service.metricCpuUtilization();
scaling.scaleOnMetric("auto-scale-cpu", {
metric: cpuUtilization,
scalingSteps: cpuScalingSteps,
Expand Down
16 changes: 9 additions & 7 deletions packages/graphql-mesh-server/lib/maintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ export class Maintenance extends Construct {
readOnly: true,
sourceVolume: "maintenanceVolume",
};
props.fargateService.taskDefinition.defaultContainer?.addMountPoints(
mountPoint
);
props.fargateService.taskDefinition.defaultContainer?.addEnvironment(
"MAINTENANCE_FILE_PATH",
`${efsVolumeMountPath}/maintenance.enabled`
);
props.fargateService.taskDefinition
.findContainer("mesh")
?.addMountPoints(mountPoint);
props.fargateService.taskDefinition
.findContainer("mesh")
?.addEnvironment(
"MAINTENANCE_FILE_PATH",
`${efsVolumeMountPath}/maintenance.enabled`
);

const api = new apigateway.RestApi(this, "maintenance-apigw");
const apiKey = api.addApiKey("maintenance-api-key", {
Expand Down

0 comments on commit 4d77dc8

Please sign in to comment.