Skip to content
This repository was archived by the owner on Mar 27, 2023. It is now read-only.

Commit 1bdca4d

Browse files
committed
Merge branch 'develop' into 'master'
Develop See merge request verbose-equals-true/django-postgres-vue-gitlab-ecs!104
2 parents 7bc1d1c + 6fccd97 commit 1bdca4d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1153
-5650
lines changed

README.md

Lines changed: 62 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -4,200 +4,126 @@ Documentation for this project can be found here:
44

55
[https://verbose-equals-true.gitlab.io/django-postgres-vue-gitlab-ecs/](https://verbose-equals-true.gitlab.io/django-postgres-vue-gitlab-ecs/)
66

7-
## Architecture
87

9-
![png](/architecture.png)
8+
## Project Architecture Overview
109

11-
## Local Development
10+
Here is an overview of the project architecture, including the CI/CD pipeline and the AWS infrastructure that will be automatically provisioned through the AWS Cloud Development Kit:
1211

13-
First, copy `.env.template` to a new file in the project's root directory called `.env`. This file will be read by `docker-compose` in the next step. Adjust any of the values in this file if needed, or add new variables for any secret information you need to pass to docker-compose (or to docker containers).
12+
![png](/architecture.png)
1413

15-
## Current Project Goals
14+
(This diagram was created with [draw.io](https://draw.io). Here's the link to the a read-only version of the diagram on draw.io: [https://drive.google.com/file/d/1gU61zjoW80fCusUcswU1zhEE5VFB1Z5U/view?usp=sharing](https://drive.google.com/file/d/1gU61zjoW80fCusUcswU1zhEE5VFB1Z5U/view?usp=sharing)
1615

17-
Currently I am working on replacing CloudFormation with CDK for infrastructure and deployment.
16+
### Legend
1817

19-
To work with CDK, do the following:
18+
1 - GitLab is used to host the source code, test the source code and deploy the application to AWS.
2019

21-
- Make sure you are using at least version 10 of node: `nvm use 13`
22-
- Activate the virtual environment with `source awscdk/.env/bin/activate`
23-
- `pip install -e awscdk` to install CDK dependencies
24-
- run `cdk synth --app awscdk/app.py --output awscdk/cdk.out` and view the resulting JSON for the nested CloudFormation stacks in `awscdk/cdk.out`
20+
2 - Unit testing (see `.gitlab-ci.yml`)
2521

26-
### Social Authentication Keys
22+
2a - Pytest
2723

28-
To use social sign on in development, you will need to create an application with the given provider.
24+
2b - Jest
2925

30-
#### GitHub
26+
2c - Cypress
3127

32-
Go to [https://github.com/settings/applications/new](https://github.com/settings/applications/new), and add the following:
28+
3 - Deployment phase (see `/gitlab-ci/aws/cdk.yml`)
3329

34-
- Application Name: A name for the development application, such as `My App Dev`
35-
- Homepage URL: `http://localhost`
36-
- Application description: (optional)
37-
- Authorization callback URL `http://localhost/auth/github/callback` (this route is defined in `quasar/src/router/routes.js`)
38-
39-
In the `.env` file, add the `Client ID` of your GitHub OAuth App as the `GITHUB_KEY` variable, and add the `Client Secret` as the `GITHUB_SECRET` variable.
30+
3a - Quasar PWA assets are built if there are changes in the `quasar` directory
4031

41-
```sh
42-
docker-compose up --build
43-
```
44-
45-
Open `http://localhost` in your browser.
46-
47-
You can specify environment variables for docker-compose by adding an `.env` file to the root of the project based on `.env.template`.
48-
49-
## VuePress Documentation
50-
51-
This project uses VuePress for documentation. To view the documentation site locally, run the following command:
52-
53-
```bash
54-
docker-compose -f compose/docs.yml up --build
55-
```
32+
3b - AWS Cloud Development Kit (CDK) defines all infrastructure in AWS (4a - 12)
5633

57-
This will make the docs available at `http://localhost:8082/docs/`. Hot-reloading through websockets is supported, so changes will show up as they are saved in your code editor.
34+
3c - AWS CLI is used to run Fargate tasks through manual GitLab CI jobs
5835

59-
### Access Django Shell in Jupyter Notebook
36+
4 - CDK Assets (ECR and S3 buckets that CDK uses internally to manage build assets and artifacts)
6037

61-
With all containers running, run the following commands:
38+
4a - Elastic Container Repository is used to manage the Django docker image used in various parts of the application
6239

63-
```
64-
docker exec -it backend bash
65-
# cd notebooks/
66-
# ../manage.py shell_plus --notebook
67-
```
40+
4b - S3 bucket used to store files associated with CDK and CloudFormation
6841

69-
or use this single command:
42+
5 - Route53 is used to route traffic to the CloudFront distribution
7043

71-
```
72-
docker exec -it backend bash -c 'cd notebooks && ../manage.py shell_plus --notebook'
73-
```
44+
6 - CloudFront distribution that serves as the "front desk" of the application. It routes requests to to the correct CloudFront Origin
7445

75-
# Running tests
46+
7 - CloudFront Origin Configurations
7647

77-
This project uses Pytest, Jest and Cypress for testing. This section describes the different ways to run tests locally.
48+
7a - S3 bucket for Quasar PWA assets
7849

79-
To run Pytest and Jest, you can use `docker-compose exec`, or you can shell into the container.
50+
7b - Application Load Balancer for Django application (`/api/`, `/admin/`, `/flower/`, `/ws/`, `/graphql/`)
8051

81-
Using `docker-compose exec`:
52+
7c - S3 bucket for Django assets (static files, public media and private media)
8253

83-
```
84-
docker-compose exec backend pytest
85-
```
54+
8 - Web server and websocket servers
8655

87-
```
88-
docker exec -it backend bash
89-
root@b24c4206002e:/code# pytest
90-
```
56+
8a - Fargate service running uvicorn process (REST, GraphQL, Django Channels)
9157

92-
To run Jest tests, you can run:
58+
8b - Autoscaling Group for Fargate Service that serves Django API
9359

94-
```
95-
docker-compose exec frontend npm run test
96-
```
60+
9 - Celery and celery worker autoscaling
9761

98-
## Integration tests
62+
9a - Fargate service that is autoscaled between 0 and `N` Fargate tasks for a given celery queue
9963

100-
Cypress can be used in an interactive mode with a UI, or it can be used in a headless way (such as in GitLab CI).
64+
9b - Scheduled Event that triggers a Lambda to make a request to Django backend which collects celery queue metrics and published metrics to CloudWatch using boto3
10165

102-
## Running Cypress Interactively
66+
9c - Lambda event the makes a request to `/api/celery-metrics/`
10367

104-
To run Cypress tests interactively, install Cypress in the root of the project:
68+
9d - CloudWatch alarm that is used to scale the Fargate service for a celery queue
10569

106-
```
107-
npm install cypress
108-
```
70+
9e - Autoscaling group for celery Fargate service
10971

110-
Then open Cypress with:
72+
10 - Fargate tasks that run Django management commands such as `migrate` and `collectstatic`. These are triggered from manual GitLab CI jobs using the AWS CLI (3c)
11173

112-
```
113-
$(npm bin)/cypress open --config baseUrl=http://localhost
114-
```
74+
11 - ElastiCache for Redis, used for Caching, Celery Broker, Channels Layer, etc.
11575

116-
Click on the button "Run all specs", and the tests will start running in a new browser window with a log of Cypress actions and test statements displayed on the left side.
76+
12 - Aurora Postgres Serverless
11777

118-
## Run Cypress in headless mode
119-
120-
There are two ways to run Cypress in headless mode. You can run Cypress against the `docker-compose` file in `compose/test.yml`, or you can run the Cypress test using `gitlab-runner`.
121-
122-
### Using `compose/test.yml`
123-
124-
To run the test locally against the production image, run the following:
125-
126-
```
127-
docker-compose -f compose/test.yml up --build
128-
```
129-
130-
This will build a special environment that resembles the environment used in GitLab CI. It brings up three containers: redis, postgres and backend. The backend serves several purposes: it runs the Django webserver, it runs the ASGI server used by Django Channels, and it runs celery tasks synchronously, and it also serves the Vue application through Django templates and static files. See the Dockerfile located at `backend/scripts/prod/Dockerfile` for details on how this works.
131-
132-
Make sure that Cypress is properly installed with:
133-
134-
```
135-
$(npm bin)/cypress verify
136-
```
78+
## Local Development
13779

138-
Then start the tests with:
80+
First, copy `.env.template` to a new file in the project's root directory called `.env`. This file will be read by `docker-compose` in the next step. Adjust any of the values in this file if needed, or add new variables for any secret information you need to pass to docker-compose (or to docker containers).
13981

140-
```
141-
$(npm bin)/cypress run --config baseUrl=http://localhost:9000
82+
```sh
83+
docker-compose up
14284
```
14385

144-
You could also run these tests in the interactive mode using `compose/test.yml` with the following command:
86+
Open `http://localhost` in your browser.
14587

146-
```
147-
$(npm bin)/cypress open --config baseUrl=http://localhost:9000
148-
```
88+
You can specify environment variables for docker-compose by adding an `.env` file to the root of the project based on `.env.template`.
14989

150-
Note that this is similar to the previous command, but we are using the `open` command instead of the `run` command.
90+
### Social Authentication Keys
15191

152-
### Using `gitlab-runner`
92+
To use social sign on in local development, you will need to create an application with the given provider such as GitHub, Google, Facebook, etc.
15393

154-
It can be useful to debug your `.gitlab-ci.yml` jobs before pushing changes to GitLab. This gives us a faster feedback loop, and it doesn't use any of the CI minutes on your free (2000 minutes/month) or paid GitLab plans.
94+
#### GitHub
15595

156-
There is a little bit of setup needed to run Cypress tests with `gitlab-runner`.
96+
Go to [https://github.com/settings/applications/new](https://github.com/settings/applications/new), and add the following:
15797

158-
First, you need to install `gitlab-runner` on your machine.
98+
- Application Name: A name for the development application, such as `My App Dev`
99+
- Homepage URL: `http://localhost`
100+
- Application description: (optional)
101+
- Authorization callback URL `http://localhost/auth/github/callback` (this route is defined in `quasar/src/router/routes.js`)
159102

160-
Next, you need to start a local registry on you r computer. Run the following command ([taken from docker documentation](https://docs.docker.com/registry/deploying/)):
103+
In the `.env` file, add the `Client ID` of your GitHub OAuth App as the `GITHUB_KEY` variable, and add the `Client Secret` as the `GITHUB_SECRET` variable.
161104

162-
```
163-
docker run -d -p 5000:5000 --restart=always --name registry registry:2
164-
```
105+
## VuePress Documentation
165106

166-
Next, you need to build the production image that you will use in the test. To do this, run the following command:
107+
This project uses VuePress for documentation. To view the documentation site locally, run the following command:
167108

168-
```
169-
docker-compose -f compose/test.yml build backend
109+
```bash
110+
docker-compose -f compose/docs.yml up --build
170111
```
171112

172-
Then tag the image with the following command:
113+
This will make the docs available at `http://localhost:8082/docs/`. Hot-reloading through websockets is supported, so changes will show up as they are saved in your code editor.
173114

174-
```
175-
docker tag compose_backend:latest localhost:5000/backend:latest
176-
```
115+
### Access Django Shell in Jupyter Notebook
177116

178-
Then push the tagged image to the local registry:
117+
With all containers running, run the following commands:
179118

180119
```
181-
docker push localhost:5000/backend:latest
120+
docker exec -it backend bash
121+
# cd notebooks/
122+
# ../manage.py shell_plus --notebook
182123
```
183124

184-
Now that we have pushed the production image to our local registry, we can run the `e2e-local` job with `gitlab-runner`.
185-
186-
To do this, we need to make sure that the `e2e-local` job defined in `.gitlab-ci.yml` is not commented. To do this, remove the `.` in front of the job name (change `.e2e-local` to `e2e-local`).
187-
188-
Then, commit your changes in git. Gitlab runner requires that you commit changes before running tests. Run the GitLab CI job with the following command:
125+
or use this single command:
189126

190127
```
191-
gitlab-runner exec docker e2e-local
128+
docker exec -it backend bash -c 'cd notebooks && ../manage.py shell_plus --notebook'
192129
```
193-
194-
Before you push your changes to GitLab, make sure that you uncomment the `e2e-local` job by adding `.` in front of it (`.e2e-local`).
195-
196-
# ToDo
197-
198-
- Add diagram of local development
199-
- Put django apps in apps folder
200-
- Redeploy django app to check settings files
201-
- Add GitLab pages site for Group project
202-
- Add file upload examples with Django REST Framework
203-
- Setup password reset

architecture.png

1.08 MB
Loading

awscdk/awscdk/awscdk.egg-info/requires.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ aws-cdk.aws_applicationautoscaling==1.42.0
55
aws-cdk.aws_certificatemanager==1.42.0
66
aws-cdk.aws_cloudwatch==1.42.0
77
aws-cdk.aws_logs==1.42.0
8+
aws-cdk.aws_lambda==1.42.0
89
aws-cdk.aws_events==1.42.0
910
aws-cdk.aws_events_targets==1.42.0
1011
aws-cdk.aws_secretsmanager==1.42.0

awscdk/awscdk/cdk_app_root.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from env_vars import Variables
1515
from static_site_bucket import StaticSiteStack
1616
from flower import FlowerServiceStack
17+
from celery_autoscaling import CeleryAutoscalingStack
1718

1819
from backend import BackendServiceStack
1920
from backend_tasks import BackendTasksStack
@@ -101,5 +102,11 @@ def __init__(
101102
self, "CeleryDefaultServiceStack"
102103
)
103104

105+
# define other celery queues here, or combine in a single construct
106+
107+
self.celery_autoscaling = CeleryAutoscalingStack(
108+
self, "CeleryAutoscalingStack"
109+
)
110+
104111
# migrate, collectstatic, createsuperuser
105112
self.backend_tasks = BackendTasksStack(self, "BackendTasksStack")

awscdk/awscdk/celery_autoscaling.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from aws_cdk import (
2+
core,
3+
aws_ec2 as ec2,
4+
aws_ecs as ecs,
5+
aws_events as events,
6+
aws_lambda,
7+
aws_events_targets as events_targets,
8+
aws_logs as logs,
9+
aws_cloudformation as cloudformation,
10+
)
11+
12+
13+
class CeleryAutoscalingStack(cloudformation.NestedStack):
14+
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
15+
super().__init__(
16+
scope, id, **kwargs,
17+
)
18+
19+
self.lambda_function = aws_lambda.Function(
20+
self,
21+
"CeleryMetricsLambdaFunction",
22+
code=aws_lambda.Code.asset("awslambda"),
23+
handler="publish_celery_metrics.lambda_handler",
24+
runtime=aws_lambda.Runtime.PYTHON_3_7,
25+
environment=scope.variables.regular_variables,
26+
)
27+
28+
self.celery_default_cw_metric_schedule = events.Rule(
29+
self,
30+
"CeleryDefaultCWMetricSchedule",
31+
schedule=events.Schedule.rate(core.Duration.minutes(5)),
32+
targets=[
33+
events_targets.LambdaFunction(handler=self.lambda_function)
34+
],
35+
)
36+
37+
# TODO: refactor this to loop through CloudWatch metrics multiple celery queues
38+
scope.celery_default_service.default_celery_queue_cw_metric.grant_put_metric_data(
39+
scope.backend_service.backend_task.task_role
40+
)

awscdk/awscdk/celery_default.py

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -85,43 +85,3 @@ def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
8585
],
8686
adjustment_type=aas.AdjustmentType.CHANGE_IN_CAPACITY,
8787
)
88-
89-
self.celery_default_cw_monitor_task = ecs.FargateTaskDefinition(
90-
self, "CeleryDefaultCWMonitoringTask"
91-
)
92-
93-
self.celery_default_cw_monitor_task.add_container(
94-
"CeleryDefaultCWMonitoringTaskContainer",
95-
image=scope.image,
96-
logging=ecs.LogDrivers.aws_logs(
97-
stream_prefix="CeleryDefaultCWMonitoringContainerLogs",
98-
log_retention=logs.RetentionDays.ONE_DAY,
99-
),
100-
environment=scope.variables.regular_variables,
101-
secrets=scope.variables.secret_variables,
102-
command=["python3", "manage.py", "put_celery_cloudwatch_metrics"],
103-
)
104-
105-
self.celery_default_cw_metric_schedule = events.Rule(
106-
self,
107-
"CeleryDefaultCWMetricSchedule",
108-
schedule=events.Schedule.rate(core.Duration.minutes(5)),
109-
targets=[
110-
events_targets.EcsTask(
111-
cluster=scope.cluster,
112-
task_definition=self.celery_default_cw_monitor_task,
113-
subnet_selection=ec2.SubnetSelection(
114-
subnet_type=ec2.SubnetType.PUBLIC
115-
),
116-
security_group=ec2.SecurityGroup.from_security_group_id(
117-
self,
118-
"CeleryDefaultCWMetricScheduleSG",
119-
security_group_id=scope.vpc.vpc_default_security_group,
120-
),
121-
)
122-
],
123-
)
124-
125-
self.default_celery_queue_cw_metric.grant_put_metric_data(
126-
self.celery_default_cw_monitor_task.task_role
127-
)

awscdk/awscdk/env_vars.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def __init__(
3232
"DEBUG": "",
3333
"FULL_DOMAIN_NAME": full_domain_name,
3434
"FULL_APP_NAME": scope.full_app_name,
35+
"CELERY_METRICS_TOKEN": "my-secret-token",
3536
"AWS_STORAGE_BUCKET_NAME": bucket_name,
3637
"POSTGRES_SERVICE_HOST": postgres_host,
3738
"POSTGRES_PASSWORD": db_secret.secret_value_from_json(

awscdk/setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"aws-cdk.aws_certificatemanager==1.42.0",
2323
"aws-cdk.aws_cloudwatch==1.42.0",
2424
"aws-cdk.aws_logs==1.42.0",
25+
"aws-cdk.aws_lambda==1.42.0",
2526
"aws-cdk.aws_events==1.42.0",
2627
"aws-cdk.aws_events_targets==1.42.0",
2728
"aws-cdk.aws_secretsmanager==1.42.0",

0 commit comments

Comments
 (0)