From ee0c68b7140ca0ad3f8e476e908f82beed0dd5d7 Mon Sep 17 00:00:00 2001 From: Gerard Clos Date: Thu, 26 Sep 2024 13:06:12 +0200 Subject: [PATCH] infra: blue/green deployments --- .github/workflows/ci.yml | 2 +- apps/infra/src/deployments/web.ts | 94 +++++++++++++++++++++++-------- 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ceb0202b..1cde93f6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: needs: [lint, test] strategy: matrix: - app: [gateway, web, websockets, workers] + app: [gateway, websockets, workers] uses: ./.github/workflows/deploy-app.yml with: app-name: ${{ matrix.app }} diff --git a/apps/infra/src/deployments/web.ts b/apps/infra/src/deployments/web.ts index 32d5e6609..c0dfd37c0 100644 --- a/apps/infra/src/deployments/web.ts +++ b/apps/infra/src/deployments/web.ts @@ -140,7 +140,7 @@ const taskDefinition = pulumi }), ) -const targetGroup = new aws.lb.TargetGroup('LatitudeLLMAppTg', { +const greenTargetGroup = new aws.lb.TargetGroup('LatitudeLLMAppTg', { port: 8080, vpcId, protocol: 'HTTP', @@ -157,31 +157,18 @@ const targetGroup = new aws.lb.TargetGroup('LatitudeLLMAppTg', { const defaultListenerArn = coreStack.requireOutput('defaultListenerArn') -new aws.lb.ListenerRule('LatitudeLLMAppListenerRule', { - listenerArn: defaultListenerArn, - actions: [ - { - type: 'forward', - targetGroupArn: targetGroup.arn, - }, - ], - conditions: [ - { - hostHeader: { - values: [DNS_ADDRESS], - }, - }, - ], -}) - const cluster = coreStack.requireOutput('cluster') as pulumi.Output -new aws.ecs.Service('LatitudeLLMApp', { + +const ecsService = new aws.ecs.Service('LatitudeLLMApp', { cluster: cluster.arn, taskDefinition: taskDefinition.arn, desiredCount: 2, launchType: 'FARGATE', forceNewDeployment: true, enableExecuteCommand: true, + deploymentController: { + type: 'CODE_DEPLOY', + }, networkConfiguration: { subnets: privateSubnets.ids, assignPublicIp: false, @@ -189,14 +176,75 @@ new aws.ecs.Service('LatitudeLLMApp', { }, loadBalancers: [ { - targetGroupArn: targetGroup.arn, + targetGroupArn: greenTargetGroup.arn, containerName, containerPort: 8080, }, ], - triggers: { - digest: image.repoDigest, - coreDigest: coreImage.repoDigest, +}) + +const blueTargetGroup = new aws.lb.TargetGroup('LatitudeLLMAppBlueTg', { + port: 8080, + vpcId, + protocol: 'HTTP', + targetType: 'ip', + healthCheck: { + path: '/api/health', + interval: 5, + timeout: 2, + healthyThreshold: 2, + unhealthyThreshold: 2, + }, + deregistrationDelay: 5, +}) + +const codeDeployApp = new aws.codedeploy.Application( + 'LatitudeLLMCodeDeployApp', + { + computePlatform: 'ECS', + }, +) + +new aws.codedeploy.DeploymentGroup('LatitudeLLMDeploymentGroup', { + appName: codeDeployApp.name, + serviceRoleArn: ecsTaskExecutionRole, + deploymentConfigName: 'CodeDeployDefault.ECSAllAtOnce', + deploymentGroupName: 'LatitudeLLMDeploymentGroup', + ecsService: { + clusterName: cluster.name, + serviceName: ecsService.name, + }, + autoRollbackConfiguration: { + enabled: true, + events: ['DEPLOYMENT_FAILURE'], + }, + blueGreenDeploymentConfig: { + deploymentReadyOption: { + actionOnTimeout: 'CONTINUE_DEPLOYMENT', + waitTimeInMinutes: 0, + }, + terminateBlueInstancesOnDeploymentSuccess: { + action: 'TERMINATE', + terminationWaitTimeInMinutes: 5, + }, + }, + deploymentStyle: { + deploymentOption: 'WITH_TRAFFIC_CONTROL', + deploymentType: 'BLUE_GREEN', + }, + loadBalancerInfo: { + targetGroupPairInfo: { + prodTrafficRoute: { + listenerArns: [defaultListenerArn], + }, + testTrafficRoute: { + listenerArns: [defaultListenerArn], + }, + targetGroups: [ + { name: blueTargetGroup.name }, + { name: greenTargetGroup.name }, + ], + }, }, })