chore: deploy prod to aws
This commit is contained in:
@@ -4,5 +4,9 @@ import { GhostV2Stack } from '../lib/cdk-stack';
|
|||||||
const app = new cdk.App();
|
const app = new cdk.App();
|
||||||
|
|
||||||
new GhostV2Stack(app, "GhostV2Stack-dev", {
|
new GhostV2Stack(app, "GhostV2Stack-dev", {
|
||||||
env: { region: "ca-central-1", account: process.env.AWS_ACCOUNT_ID! },
|
env: { region: "ca-central-1", account: process.env.AWS_ACCOUNT_ID! }, environment: "dev"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
new GhostV2Stack(app, "GhostV2Stack-prod", {
|
||||||
|
env: { region: "ca-central-1", account: process.env.AWS_ACCOUNT_ID!, }, environment: "prod"
|
||||||
|
})
|
||||||
|
|||||||
7
cdk/cdk.context.json
Normal file
7
cdk/cdk.context.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"availability-zones:account=585061171043:region=ca-central-1": [
|
||||||
|
"ca-central-1a",
|
||||||
|
"ca-central-1b",
|
||||||
|
"ca-central-1d"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,20 +1,156 @@
|
|||||||
import * as cdk from 'aws-cdk-lib/core';
|
import * as cdk from "aws-cdk-lib/core";
|
||||||
import { Construct } from 'constructs';
|
import { Construct } from "constructs";
|
||||||
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
|
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
|
||||||
|
import * as ec2 from "aws-cdk-lib/aws-ec2";
|
||||||
|
import * as ecs from "aws-cdk-lib/aws-ecs";
|
||||||
|
import * as ssm from "aws-cdk-lib/aws-ssm"
|
||||||
|
import * as servicediscovery from "aws-cdk-lib/aws-servicediscovery";
|
||||||
|
import * as apigatewayv2 from "aws-cdk-lib/aws-apigatewayv2";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
interface StackProps extends cdk.StackProps {
|
||||||
|
environment: string
|
||||||
|
}
|
||||||
|
|
||||||
export class GhostV2Stack extends cdk.Stack {
|
export class GhostV2Stack extends cdk.Stack {
|
||||||
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
constructor(scope: Construct, id: string, props: StackProps) {
|
||||||
super(scope, id, props);
|
super(scope, id, props);
|
||||||
|
|
||||||
const table = new dynamodb.TableV2(this, "ghostv2-table", {
|
const table = new dynamodb.TableV2(this, "ghostv2-table", {
|
||||||
partitionKey: { name: "pk", type: dynamodb.AttributeType.STRING },
|
partitionKey: { name: "pk", type: dynamodb.AttributeType.STRING },
|
||||||
sortKey: { name: "sk", type: dynamodb.AttributeType.STRING },
|
sortKey: { name: "sk", type: dynamodb.AttributeType.STRING },
|
||||||
billing: dynamodb.Billing.onDemand(),
|
billing: dynamodb.Billing.onDemand(),
|
||||||
})
|
});
|
||||||
table.addGlobalSecondaryIndex({
|
table.addGlobalSecondaryIndex({
|
||||||
indexName: "gsi1",
|
indexName: "gsi1",
|
||||||
partitionKey: { name: "gsi1pk", type: dynamodb.AttributeType.STRING },
|
partitionKey: { name: "gsi1pk", type: dynamodb.AttributeType.STRING },
|
||||||
sortKey: { name: "gsi1sk", type: dynamodb.AttributeType.STRING }
|
sortKey: { name: "gsi1sk", type: dynamodb.AttributeType.STRING },
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const vpc = new ec2.Vpc(this, "ghostv2 vpc", {
|
||||||
|
maxAzs: 2,
|
||||||
|
natGateways: 0,
|
||||||
|
subnetConfiguration: [
|
||||||
|
{
|
||||||
|
name: "Public",
|
||||||
|
subnetType: ec2.SubnetType.PUBLIC,
|
||||||
|
cidrMask: 24,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const ecsSecurityGroup = new ec2.SecurityGroup(this, "ghostv2 security group", {
|
||||||
|
vpc,
|
||||||
|
description: "Security group for ECS Fargate tasks",
|
||||||
|
allowAllOutbound: true,
|
||||||
|
});
|
||||||
|
ecsSecurityGroup.addIngressRule(
|
||||||
|
ec2.Peer.ipv4(vpc.vpcCidrBlock),
|
||||||
|
ec2.Port.tcp(8080),
|
||||||
|
"Allow traffic from VPC"
|
||||||
|
);
|
||||||
|
|
||||||
|
const cluster = new ecs.Cluster(this, "ghostv2 cluster", {
|
||||||
|
vpc,
|
||||||
|
});
|
||||||
|
const namespace = new servicediscovery.PrivateDnsNamespace(
|
||||||
|
this,
|
||||||
|
"ghostv2 namespace",
|
||||||
|
{
|
||||||
|
name: "ghostv2.local",
|
||||||
|
vpc,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const taskDefinition = new ecs.FargateTaskDefinition(
|
||||||
|
this,
|
||||||
|
"ghostv2 api",
|
||||||
|
{
|
||||||
|
memoryLimitMiB: 512,
|
||||||
|
cpu: 256,
|
||||||
|
runtimePlatform: {
|
||||||
|
cpuArchitecture: ecs.CpuArchitecture.X86_64,
|
||||||
|
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
table.grantReadWriteData(taskDefinition.taskRole);
|
||||||
|
|
||||||
|
const secretImports = ["DATABASE_URL", "GH_CLIENT_ID", "GH_CLIENT_SECRET", "REPOS_TABLE_NAME", "SENTRY_DSN"];
|
||||||
|
const secrets = secretImports.reduce<Record<string, ecs.Secret>>((acc, name) => {
|
||||||
|
acc[name] = ecs.Secret.fromSsmParameter(ssm.StringParameter.fromSecureStringParameterAttributes(this, `param${name}`, {
|
||||||
|
parameterName: `/ghostv2/${props.environment}/${name}`
|
||||||
|
}))
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
taskDefinition.addContainer("api", {
|
||||||
|
image: ecs.ContainerImage.fromAsset(path.join(__dirname, "../../api")),
|
||||||
|
environment: {
|
||||||
|
RUST_LOG: "info",
|
||||||
|
},
|
||||||
|
portMappings: [
|
||||||
|
{
|
||||||
|
containerPort: 8080,
|
||||||
|
protocol: ecs.Protocol.TCP,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
secrets
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = new ecs.FargateService(this, "ghostv2 service", {
|
||||||
|
cluster,
|
||||||
|
taskDefinition,
|
||||||
|
desiredCount: 1,
|
||||||
|
assignPublicIp: true,
|
||||||
|
vpcSubnets: {
|
||||||
|
subnetType: ec2.SubnetType.PUBLIC,
|
||||||
|
},
|
||||||
|
securityGroups: [ecsSecurityGroup],
|
||||||
|
capacityProviderStrategies: [
|
||||||
|
{
|
||||||
|
capacityProvider: "FARGATE_SPOT",
|
||||||
|
weight: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cloudMapOptions: {
|
||||||
|
name: "api",
|
||||||
|
cloudMapNamespace: namespace,
|
||||||
|
dnsRecordType: servicediscovery.DnsRecordType.SRV,
|
||||||
|
containerPort: 8080,
|
||||||
|
dnsTtl: cdk.Duration.seconds(10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const vpcLink = new apigatewayv2.CfnVpcLink(this, "ghostv2 vpc link", {
|
||||||
|
name: "ghostv2-vpc-link",
|
||||||
|
subnetIds: vpc.publicSubnets.map((s) => s.subnetId),
|
||||||
|
securityGroupIds: [ecsSecurityGroup.securityGroupId],
|
||||||
|
});
|
||||||
|
|
||||||
|
const httpApi = new apigatewayv2.CfnApi(this, "ghostv2 http-api", {
|
||||||
|
name: "ghostv2-api",
|
||||||
|
protocolType: "HTTP",
|
||||||
|
});
|
||||||
|
|
||||||
|
const integration = new apigatewayv2.CfnIntegration(this, "Integration", {
|
||||||
|
apiId: httpApi.ref,
|
||||||
|
integrationType: "HTTP_PROXY",
|
||||||
|
integrationMethod: "ANY",
|
||||||
|
integrationUri: service.cloudMapService!.serviceArn,
|
||||||
|
connectionType: "VPC_LINK",
|
||||||
|
connectionId: vpcLink.ref,
|
||||||
|
payloadFormatVersion: "1.0",
|
||||||
|
});
|
||||||
|
|
||||||
|
new apigatewayv2.CfnRoute(this, "DefaultRoute", {
|
||||||
|
apiId: httpApi.ref,
|
||||||
|
routeKey: "$default",
|
||||||
|
target: `integrations/${integration.ref}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
new apigatewayv2.CfnStage(this, "Stage", {
|
||||||
|
apiId: httpApi.ref,
|
||||||
|
stageName: "$default",
|
||||||
|
autoDeploy: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
scripts/import-env.sh
Executable file
27
scripts/import-env.sh
Executable file
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
PREFIX="/ghostv2"
|
||||||
|
REGION="ca-central-1"
|
||||||
|
WRITE="$1"
|
||||||
|
|
||||||
|
while getopts "w" opt; do
|
||||||
|
case $opt in
|
||||||
|
w) WRITE=true ;;
|
||||||
|
*) echo "Usage $0 [-w]" && exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
||||||
|
|
||||||
|
echo "Creating parameter: $PREFIX/$key"
|
||||||
|
|
||||||
|
if [[ "$WRITE" == "true" ]]; then
|
||||||
|
aws ssm put-parameter \
|
||||||
|
--name "$PREFIX/$key" \
|
||||||
|
--value "$value" \
|
||||||
|
--type SecureString \
|
||||||
|
--overwrite \
|
||||||
|
--region "$REGION" > /dev/null
|
||||||
|
fi
|
||||||
|
done < api/.env
|
||||||
Reference in New Issue
Block a user