Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,29 @@ and run the migrations:
uv run alembic upgrade head
```

# Job scheduler

We use the `apscheduler` library to schedule recurring jobs. Currently
this is used to synchronize user information from Auth0 to the AAI
database. You can run the job scheduler locally using:

```shell
uv run python run_scheduler.py
```

Note that for small jobs that run on-demand, e.g. sending a notification email when a user
signs up, you can use FastAPI's built-in [background tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/)
within the FastAPI app, instead of using the dedicated job scheduler.

# Deployment

Currently the service is deployed to AWS via the CDK scripts in `deploy/`,
and updated on each commit to `main`.

Secrets/configuration variables for the deployment are stored in the
GitHub Secrets for the repository.

The service deploys two containers (which both use the same image/Python environment):

* The FastAPI app
* The `apscheduler` job scheduler
33 changes: 30 additions & 3 deletions deploy/aai_backend_deploy/aai_backend_deploy_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ def __init__(self, scope: Construct, construct_id: str, config: dict, **kwargs)

# Task definition for Fargate
task_definition = ecs.FargateTaskDefinition(self, "AaiBackendTaskDef",
memory_limit_mib=1024,
cpu=512)
# Allow executing comands in the ECS container
memory_limit_mib=2048,
cpu=1024)
# Allow executing commands in the ECS container
task_definition.task_role.add_managed_policy(
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore")
)
Expand All @@ -85,6 +85,25 @@ def __init__(self, scope: Construct, construct_id: str, config: dict, **kwargs)
},
logging=ecs.LogDrivers.aws_logs(stream_prefix="FastAPI"),
)
# Run the job scheduler as a separate container
task_definition.add_container(
"SchedulerContainer",
image=ecs.ContainerImage.from_ecr_repository(
ecr_repo,
tag="latest"
),
# Override the default command to run the scheduler
command=["uv", "run", "python", "run_scheduler.py"],
environment={
"FORCE_REDEPLOY": str(datetime.datetime.now()),
"DB_HOST": self.db_host,
},
secrets={
"DB_USER": ecs.Secret.from_secrets_manager(db_secret, field="username"),
"DB_PASSWORD": ecs.Secret.from_secrets_manager(db_secret, field="password"),
},
logging=ecs.LogDrivers.aws_logs(stream_prefix="Scheduler"),
)

container.add_port_mappings(
ecs.PortMapping(container_port=8000)
Expand Down Expand Up @@ -112,4 +131,12 @@ def __init__(self, scope: Construct, construct_id: str, config: dict, **kwargs)

service.target_group.configure_health_check(path="/", healthy_http_codes="200-399")

# Output relevant ARNs and IDs
CfnOutput(self, "LoadBalancerDNS", value=service.load_balancer.load_balancer_dns_name)
CfnOutput(self, "ServiceArn", value=service.service.service_arn)
CfnOutput(self, "ServiceName", value=service.service.service_name)
CfnOutput(self, "ClusterArn", value=cluster.cluster_arn)
CfnOutput(self, "ClusterName", value=cluster.cluster_name)
CfnOutput(self, "TaskDefinitionArn", value=task_definition.task_definition_arn)
CfnOutput(self, "TaskDefinitionFamily", value=task_definition.family)
CfnOutput(self, "LoadBalancerArn", value=service.load_balancer.load_balancer_arn)