Skip to content

Commit 91dc9d8

Browse files
authored
Merge pull request #59 from hallieswan/MG-182
MG-182: update agora infra based on improvements to model-ad infra
2 parents dd47a80 + 847a78b commit 91dc9d8

File tree

10 files changed

+141
-15
lines changed

10 files changed

+141
-15
lines changed

.env.example

Lines changed: 0 additions & 2 deletions
This file was deleted.

.github/workflows/aws-deploy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
role-session-name: ${{ inputs.role-session-name }}
4949
role-duration-seconds: ${{ inputs.role-duration-seconds }}
5050
- name: CDK deploy
51-
run: cdk deploy --all --concurrency 5 --require-approval never
51+
run: cdk deploy --all --debug --concurrency 5 --require-approval never
5252
env:
5353
ENV: ${{ inputs.environment }}
5454
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ jobs:
2929
env:
3030
ENV: ${{ inputs.environment }}
3131
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32-
run: cdk synth --output ./cdk.out
32+
run: cdk synth --debug --output ./cdk.out

README.md

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11

22
# AWS CDK app
33

4-
AWS CDK app for deploying Agora.
4+
A Github template using the AWS CDK to create an ECS infrastructure project for deploying Agora.
55

66
# Prerequisites
77

88
AWS CDK projects require some bootstrapping before synthesis or deployment.
99
Please review the [bootstapping documentation](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_bootstrap)
1010
before development.
1111

12+
> [!Note]
13+
> Sage IT deploys this CDK bootstrap upon creation of every AWS account in our AWS Organization.
14+
1215
# Dev Container
1316

1417
This repository provides a [dev container](https://containers.dev/) that includes all the tools
@@ -80,7 +83,12 @@ Please install pre-commit, once installed the file validations will
8083
automatically run on every commit. Alternatively you can manually
8184
execute the validations by running `pre-commit run --all-files`.
8285

83-
Create a [GitHub classic PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with `read:packages` access, then create a .env file using .env.example as a template.
86+
Create a [GitHub classic personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) with `read:packages` access. The token is used to authenticate with the GitHub API and access the packages endpoint to look up the latest image version for each service. Create an `.env` file with the following variables:
87+
88+
```
89+
GITHUB_TOKEN="your-token-here"
90+
ENV="dev"
91+
```
8492

8593
Verify CDK to Cloudformation conversion by running [cdk synth]:
8694

@@ -99,7 +107,7 @@ python -m pytest tests/ -s -v
99107
```
100108

101109

102-
# Environments
110+
## Environments
103111

104112
An `ENV` environment variable must be set when running the `cdk` command tell the
105113
CDK which environment's variables to use when synthesising or deploying the stacks.
@@ -110,7 +118,7 @@ Set environment variables for each environment in the [app.py](./app.py) file:
110118
environment_variables = {
111119
"VPC_CIDR": "10.254.192.0/24",
112120
"FQDN": "dev.app.io",
113-
"CERTIFICATE_ARN": "arn:aws:acm:us-east-1:XXXXXXXXXXX:certificate/0e9682f6-3ffa-46fb-9671-b6349f5164d6",
121+
"CERTIFICATE_ID": "0e9682f6-3ffa-46fb-9671-b6349f5164d6",
114122
"TAGS": {"CostCenter": "NO PROGRAM / 000000"},
115123
}
116124
```
@@ -121,15 +129,20 @@ For example, synthesis with the `prod` environment variables:
121129
ENV=prod cdk synth
122130
```
123131

124-
# Certificates
132+
> [!NOTE]
133+
> The `VPC_CIDR` must be a unique value within our AWS organization. Check our
134+
> [wiki](https://sagebionetworks.jira.com/wiki/spaces/IT/pages/2850586648/Setup+AWS+VPC)
135+
> for information on how to obtain a unique CIDR
136+
137+
## Certificates
125138

126139
Certificates to set up HTTPS connections should be created manually in AWS certificate manager.
127140
This is not automated due to AWS requiring manual verification of the domain ownership.
128141
Once created take the ARN of the certificate and set that ARN in environment_variables.
129142

130143
![ACM certificate](docs/acm-certificate.png)
131144

132-
# Secrets
145+
## Secrets
133146

134147
Secrets can be manually created in the
135148
[AWS Secrets Manager](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html).
@@ -185,11 +198,61 @@ import os
185198

186199
my_secret = os.environ.get("SINGLE_VALUE_SECRET", None)
187200
```
188-
201+
![Secrets Manager secret](docs/secrets-manager-secret.png)
189202

190203
> [!NOTE]
191204
> Retrieving secrets requires access to the AWS Secrets Manager
192205
206+
## DNS
207+
208+
A DNS CNAME must be created in org-formation after the initial
209+
deployment of the application to make the application available at the desired
210+
URL. The CDK application exports the DNS name of the Application Load Balancer
211+
to be consumed in org-formation. [An example PR setting up a CNAME](https://github.com/Sage-Bionetworks-IT/organizations-infra/pull/1299).
212+
213+
Login to the AWS cloudformation console and navigate to the deployed stack `app-load-balancer`
214+
and click on the `Outputs` tab. On the row whose key is `LoadBalancerDNS` look for the
215+
value in the `Export Name` column, e.g., `app-dev-load-balancer-dns`.
216+
![Cloudformation Load Balancer](docs/cloudformation-load-balancer.png)
217+
218+
Now use the name in the `TargetHostName` definition, for example:
219+
220+
```
221+
TargetHostName: !CopyValue [!Sub 'app-dev-load-balancer-dns', !Ref DnTDevAccount]
222+
```
223+
224+
(You would also replace `DnTDevAccount` with the name of the account in which the application is deployed.)
225+
226+
> [!NOTE]
227+
> Setting up the DNS cname should be done at the very end of this infra setup
228+
229+
230+
## Debugging
231+
232+
Generally CDK deployments will create cloudformation events during a CDK deploy.
233+
The events can be viewed in the AWS console under the cloudformation service page.
234+
Viewing those events will help with errors during a deployment. Below are cases
235+
where it might be difficult to debug due to misleading or insufficient error
236+
messages from AWS
237+
238+
### Missing Secrets
239+
240+
Each new environment (dev/staging/prod/etc..) may require adding secrets. If a
241+
secret is not created for the environment you may get an error with the following
242+
stack trace..
243+
```
244+
Resource handler returned message: "Error occurred during operation 'ECS Deployment Circuit Breaker was triggered'." (RequestToken: d180e115-ba94-d8a2-acf9-abe17a3aaed9, HandlerErrorCode: GeneralServiceException)
245+
new BaseService (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/jsii-kernel-4PEWmj/node_modules/aws-cdk-lib/aws-ecs/lib/base/base-service.js:1:3583)
246+
\_ new FargateService (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/jsii-kernel-4PEWmj/node_modules/aws-cdk-lib/aws-ecs/lib/fargate/fargate-service.js:1:967)
247+
\_ new ApplicationLoadBalancedFargateService (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/jsii-kernel-4PEWmj/node_modules/aws-cdk-lib/aws-ecs-patterns/lib/fargate/application-load-balanced-fargate-service.js:1:2300)
248+
\_ Kernel._create (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/tmpqkmckdm2/lib/program.js:9964:29)
249+
\_ Kernel.create (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/tmpqkmckdm2/lib/program.js:9693:29)
250+
\_ KernelHost.processRequest (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/tmpqkmckdm2/lib/program.js:11544:36)
251+
\_ KernelHost.run (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/tmpqkmckdm2/lib/program.js:11504:22)
252+
\_ Immediate._onImmediate (/private/var/folders/qr/ztb40vmn2pncyh8jpsgfnrt40000gp/T/tmpqkmckdm2/lib/program.js:11505:46)
253+
\_ processImmediate (node:internal/timers:464:21)
254+
```
255+
193256
# Deployment
194257

195258
## Bootstrap
@@ -269,7 +332,6 @@ AWS_PROFILE=itsandbox-dev AWS_DEFAULT_REGION=us-east-1 aws ecs execute-command \
269332
--command "/bin/sh" --interactive
270333
```
271334

272-
273335
# CI Workflow
274336

275337
This repo has been set up to use Github Actions CI to continuously deploy the application.
@@ -284,3 +346,66 @@ The workflow for continuous integration:
284346
* CI deploys changes to the staging environment (stage.app.io) in the AWS prod account.
285347
* Changes are promoted (or merged) to the git prod branch.
286348
* CI deploys changes to the prod environment (prod.app.io) in the AWS prod account.
349+
350+
![CI deployment workflow](docs/ci-deployment-workflow.png)
351+
352+
# Deployment Process
353+
354+
## Overview
355+
356+
The source code for the application lives in the [sage-monorepo](https://github.com/Sage-Bionetworks/sage-monorepo). When a new git tag is added in the monorepo, images are published to [GHCR](https://github.com/orgs/Sage-Bionetworks/packages?tab=packages&q=agora). These images are deployed with GHA to AWS Fargate using infrastructure code in this repo.
357+
358+
### Dev
359+
360+
| ![Tags on latest image published by sage-monorepo](docs/dev-package-tags.png) |
361+
| :---: |
362+
| Tags on latest image published by sage-monorepo |
363+
364+
The development environment was created so that we can redeploy easily. Whenever code in the sage-monorepo is merged after PR was reviewed and all checks approved, a GHA job publishes new images tagged with `edge` and the commit SHA for each service in the stack that changed (e.g. see screenshot of [agora-app package](https://github.com/sage-bionetworks/sage-monorepo/pkgs/container/agora-app) above). The dev infra stack points at the `edge` image tag. So we can rerun the latest `deploy-dev` GHA job to redeploy the latest app code, by looking up the SHA of the most recent image using the `edge` tag and the GitHub API.
365+
366+
So, the footer of the dev site will only show the image tag of the `agora-app` package, which in this case is the commit SHA.
367+
368+
### Stage/Prod
369+
370+
| ![git tag on image published by sage-monorepo](docs/stageprod-package-tags.png) |
371+
| :---: |
372+
| git tag on image published by sage-monorepo |
373+
374+
Stage and prod environments point at a specific git tag that is manually added. As described below, when a new git tag is manually created for this app in the sage-monorepo, a GHA job publishes new images tagged with that git tag (see screenshot above). Then, the stage or prod environment variavles in the infra repo can be updated to point at that tag. When the changes are merged to the `stage` or `prod` branch, a GHA job will run the `deploy-stage` or `deploy-prod` job accordingly, which will deploy the images tagged with the git tag.
375+
376+
## Dev Deployment
377+
378+
1. When a PR that affects this app is merged to `main` in the sage-monorepo, wait for the [ci job](https://github.com/Sage-Bionetworks/sage-monorepo/actions/workflows/ci.yml) to finish building and publishing images for the affected projects to GHCR.
379+
2. Rerun the last [deploy-dev job](https://github.com/Sage-Bionetworks-IT/agora-infra-v3/actions/workflows/deploy-dev.yaml) and wait for the job to successfully update dev deployment. Deployment can be monitored in AWS console in AWS ECS.
380+
3. Confirm that [dev site](https://agora-dev.adknowledgeportal.org/) shows changes from last merged PR.
381+
382+
## Staging Deployment
383+
384+
1. Review the list of existing tags [here](https://github.com/Sage-Bionetworks/sage-monorepo/tags). Identify the next `agora` tag. For example, if the last `agora` tag is `agora/v4.0.0-rc2`, then the next tag will be `agora/v4.0.0-rc3`. If the last `agora` tag doesn’t have a release candidate suffix (e.g. `agora/v4.0.0`), then the next tag will be the first release candidate of a new version (e.g. `agora/v4.0.1-rc1`). This follows the convention found at [semver.org](https://semver.org/).
385+
2. Create a new git tag in the sage-monorepo:
386+
- Open devcontainer
387+
- Checkout the main branch: `git checkout main`
388+
- Fetch latest changes: `git fetch upstream`
389+
- Rebase: `git rebase upstream/main`
390+
- Tag the commit: `git tag agora/v4.0.0-rc3`
391+
- Push the tag: `git push upstream tag agora/v4.0.0-rc3`
392+
3. Wait for sage-monorepo [release GHA job](https://github.com/Sage-Bionetworks/sage-monorepo/actions/workflows/release.yml) to successfully build, tag, and push images to GHCR.
393+
4. Create PR in this repo **to the dev branch** that sets `GHCR_PACKAGE_VERSION` for stage and prod environments to the new version number in `app.py`, since the images are only tagged with the version number (e.g. `4.0.0-rc3`) rather than the full tag name (e.g. `agora/v4.0.0-rc3` ).
394+
5. Merge PR. Wait for [deploy-dev job](https://github.com/Sage-Bionetworks-IT/agora-infra-v3/actions/workflows/deploy-dev.yaml) to successfully update dev deployment. Deployment can be monitored in AWS console in AWS ECS.
395+
6. Create PR in this repo **to merge dev into the stage branch**.
396+
7. Merge PR. Wait for [deploy-stage GHA job](https://github.com/Sage-Bionetworks-IT/modeladexplorer-v3/actions/workflows/deploy-stage.yaml) to successfully update staging deployment. Deployment can be monitored in AWS console in AWS ECS.
397+
8. Confirm that [staging site](https://agora-stage.adknowledgeportal.org/) shows new version’s tag in the app footer.
398+
399+
## Production Deployment
400+
401+
1. Go to the staging site and note the tag in the app footer. Identify the `agora` release tag. For example, if the staging site tag is `agora/v4.0.0-rc3`, then the release version will be `agora/release/v4.0.0`.
402+
2. Create a new git tag in the sage-monorepo:
403+
- Open devcontainer
404+
- Checkout the main branch: `git checkout main`
405+
- Fetch latest changes: `git fetch upstream`
406+
- Rebase: `git rebase upstream/main`
407+
- Tag the commit: `git tag agora/release/v4.0.0`
408+
- Push the tag: `git push upstream tag agora/release/v4.0.0`
409+
3. Create a PR in this repo **to merge the stage branch into the prod branch**.
410+
4. Merge PR. Wait for the [deploy-prod GHA job](https://github.com/Sage-Bionetworks-IT/modeladexplorer-v3/actions/workflows/deploy-prod.yaml) to successfully update prod deployment. Deployment can be monitored in AWS console in AWS ECS.
411+
5. Confirm that [production site](https://agora.adknowledgeportal.org/) shows the same tag in the app footer as the staging site.

app.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"CERTIFICATE_ID": "69b3ba97-b382-4648-8f94-a250b77b4994",
2626
"TAGS": {"CostCenter": "AMP-AD DCC / 101500", "Environment": "prod"},
2727
"AUTO_SCALE_CAPACITY": {"min": 2, "max": 4},
28+
"GHCR_PACKAGE_VERSION": "4.0.0-rc4",
2829
}
2930
case "stage":
3031
environment_variables = {
@@ -33,6 +34,7 @@
3334
"CERTIFICATE_ID": "69b3ba97-b382-4648-8f94-a250b77b4994",
3435
"TAGS": {"CostCenter": "AMP-AD DCC / 101500", "Environment": "stage"},
3536
"AUTO_SCALE_CAPACITY": {"min": 2, "max": 4},
37+
"GHCR_PACKAGE_VERSION": "4.0.0-rc4",
3638
}
3739
case "dev":
3840
environment_variables = {
@@ -41,6 +43,7 @@
4143
"CERTIFICATE_ID": "e8093404-7db1-4042-90d0-01eb5bde1ffc",
4244
"TAGS": {"CostCenter": "AMP-AD DCC / 101500", "Environment": "dev"},
4345
"AUTO_SCALE_CAPACITY": {"min": 1, "max": 2},
46+
"GHCR_PACKAGE_VERSION": "edge",
4447
}
4548
case _:
4649
valid_envs_str = ",".join(VALID_ENVIRONMENTS)
@@ -51,13 +54,13 @@
5154
stack_name_prefix = f"agora-{environment}"
5255
fully_qualified_domain_name = environment_variables["FQDN"]
5356
environment_tags = environment_variables["TAGS"]
54-
agora_version = "edge"
57+
ghcr_package_version = environment_variables["GHCR_PACKAGE_VERSION"]
5558
docdb_master_username = "master"
5659
mongodb_port = 27017
5760
vpn_cidr = "10.1.0.0/16"
5861

5962
# Get image versions
60-
if agora_version == "edge":
63+
if ghcr_package_version == "edge":
6164
app_version = get_alternate_tag_for_edge_package_version(
6265
"Sage-Bionetworks", "agora-app"
6366
)
@@ -68,7 +71,7 @@
6871
"Sage-Bionetworks", "agora-apex"
6972
)
7073
else:
71-
app_version = api_version = apex_version = agora_version
74+
app_version = api_version = apex_version = ghcr_package_version
7275

7376
print(
7477
f"Using images: agora-app:{app_version}, agora-api:{api_version}, agora-apex:{apex_version}"

docs/ci-deployment-workflow.png

143 KB
Loading
151 KB
Loading

docs/dev-package-tags.png

27.3 KB
Loading

docs/secrets-manager-secret.png

180 KB
Loading

docs/stageprod-package-tags.png

19.3 KB
Loading

0 commit comments

Comments
 (0)