From bdc6171ba0d1365f4661a1d29bb4b7b21e3f8b72 Mon Sep 17 00:00:00 2001 From: Grace Whitney Date: Fri, 13 Dec 2024 15:51:07 -0500 Subject: [PATCH 1/3] Add worker server and redis to terraform --- deploy.py | 99 ++++++++++++++----- terraform/envs/production/main.tf | 34 +++++-- terraform/envs/staging/main.tf | 34 +++++-- terraform/modules/ecs_deployment/ecs.tf | 58 ++++++++++- .../modules/ecs_deployment/elasticache.tf | 31 ++++++ terraform/modules/ecs_deployment/iam.tf | 3 +- terraform/modules/ecs_deployment/locals.tf | 3 + terraform/modules/ecs_deployment/outputs.tf | 27 +++-- .../modules/ecs_deployment/secrets_manager.tf | 1 + .../modules/ecs_deployment/security_groups.tf | 47 ++++++++- terraform/modules/ecs_deployment/variables.tf | 22 ++++- 11 files changed, 309 insertions(+), 50 deletions(-) create mode 100644 terraform/modules/ecs_deployment/elasticache.tf diff --git a/deploy.py b/deploy.py index 4a73dff..a86e7e0 100644 --- a/deploy.py +++ b/deploy.py @@ -53,11 +53,13 @@ def deploy(args): if args.skip_migration: logging.info("Skipping database migration") else: + # Stop worker before running migrations + stop_worker_service(env) # Run and wait for migrations run_migrations(args.env) # Redeploy services - restart_web_service(args.env) + restart_services(args.env) def setup(envs): @@ -185,42 +187,91 @@ def run_migrations(env): ) raise MigrationTimeOut() - -def restart_web_service(env): - # Restart ECS web service to deploy new code +def stop_worker_service(env): + logging.info("Stopping worker service") + cluster_id = get_terraform_output("cluster_id", env) + service_name = get_terraform_output("worker_service_name", env) ecs_client = boto3.session.Session(profile_name=AWS_PROFILE_NAME, region_name=AWS_REGION).client("ecs") + ecs_client.update_service( + cluster=cluster_id, + service=service_name, + desiredCount=0 + ) + # Desired status may not have updated immediately -- try both RUNNING and STOPPED + task_arns = ecs_client.list_tasks(cluster=cluster_id, serviceName=service_name)["taskArns"] + if not task_arns: + task_arns = ecs_client.list_tasks( + cluster=cluster_id, serviceName=service_name, desiredStatus="STOPPED" + )["taskArns"] + if not task_arns: + logging.info("No worker tasks to stop") + return + while True: + tasks = ecs_client.describe_tasks(cluster=cluster_id, tasks=task_arns)["tasks"] + if all(task.get("stoppedAt") for task in tasks): + break + logging.info("Waiting for worker service to stop...") + time.sleep(STATUS_CHECK_INTERVAL) + logging.info("Worker service stopped") + +def restart_services(env): + # Restarts both web and worker in parallel (order doesn't matter since communication is handled via redis) + # Restart ECS web service to deploy new code logging.info("Redeploying web service...") + web_service_name = get_terraform_output("web_service_name", env) cluster_id = get_terraform_output("cluster_id", env) - service_name = get_terraform_output("web_service_name", env) + ecs_client = boto3.session.Session(profile_name=AWS_PROFILE_NAME, region_name=AWS_REGION).client("ecs") ecs_client.update_service( cluster=cluster_id, - service=service_name, - forceNewDeployment=True + service=web_service_name, + forceNewDeployment=True, + ) + # Restart worker service to deploy new code and set the desired task count back to expected state + logging.info("Redeploying worker service...") + worker_service_name = get_terraform_output("worker_service_name", env) + desired_task_count = get_terraform_output("worker_task_desired_count", env) + ecs_client.update_service( + cluster=cluster_id, + service=worker_service_name, + forceNewDeployment=True, + desiredCount=int(desired_task_count) ) - while True: logging.info("Waiting for deployment to finish...") - services_response = ecs_client.describe_services(cluster=cluster_id, services=[service_name]) - deployments = services_response["services"][0]["deployments"] - new_deployment = next(deployment for deployment in deployments if deployment["status"] == "PRIMARY") - deployment_state = new_deployment["rolloutState"] - if deployment_state == "IN_PROGRESS": + services_response = ecs_client.describe_services( + cluster=cluster_id, services=[web_service_name, worker_service_name] + ) + in_progress = False + failure = False + for service in services_response["services"]: + new_deployment = next( + deployment for deployment in service["deployments"] if deployment["status"] == "PRIMARY" + ) + deployment_state = new_deployment["rolloutState"] + service_name = service['serviceName'] + if deployment_state == "IN_PROGRESS": + in_progress = True + if deployment_state == "COMPLETED": + logging.info(f"Success! Deployment complete for service {service_name}.") + elif deployment_state == "FAILED": + is_worker = service_name == worker_service_name + logging.error( + f"Deployment failed for {service_name}! Reason: {new_deployment['rolloutStateReason']}. " + f"Check log stream for more info: {cloudwatch_log_url(env, worker=is_worker)}" + ) + failure = True + else: + logging.warning(f"Unknown deployment state {deployment_state}. Please check the ECS console.") + failure = True + if in_progress and not failure: time.sleep(STATUS_CHECK_INTERVAL) continue - if deployment_state == "COMPLETED": - logging.info("Success! Deployment complete.") - elif deployment_state == "FAILED": - logging.error( - f"Deployment failed! Reason: {new_deployment['rolloutStateReason']}. " - f"Check log stream for more info: {cloudwatch_log_url(env)}" - ) - else: - logging.warning(f"Unknown deployment state {deployment_state}. Please check the ECS console.") break -def cloudwatch_log_url(env): - cloudwatch_log_group_name = get_terraform_output("cloudwatch_log_group_name", env) +def cloudwatch_log_url(env, worker=False): + log_name_key = "worker_log_group_name" if worker else "web_log_group_name" + cloudwatch_log_group_name = get_terraform_output(log_name_key, env) return f"https://{AWS_REGION}.console.aws.amazon.com/cloudwatch/home?region={AWS_REGION}#logsV2:log-groups/log-group/{cloudwatch_log_group_name}" diff --git a/terraform/envs/production/main.tf b/terraform/envs/production/main.tf index 017113e..a127cbe 100644 --- a/terraform/envs/production/main.tf +++ b/terraform/envs/production/main.tf @@ -41,7 +41,7 @@ module "ecs_deployment" { rds_multi_az = true container_web_cpu = 1024 container_web_memory = 1024 - container_count = 2 + container_web_count = 2 ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-FIPS-2023-04" } @@ -50,9 +50,9 @@ output "cluster_id" { value = module.ecs_deployment.cluster_id } -output "cloudwatch_log_group_name" { - description = "The name of the cloudwatch log group for the web service task" - value = module.ecs_deployment.cloudwatch_log_group_name +output "ecr_image_uri" { + description = "ECR URI where the environment's image is stored" + value = module.ecs_deployment.ecr_image_uri } output "ecr_repository_name" { @@ -66,14 +66,13 @@ output "public_ip" { } output "web_service_name" { - description = "The name of the ECS container running the web service" + description = "The name of the ECS web service. This is also the container name." value = module.ecs_deployment.web_service_name } output "web_network_configuration_security_group" { - description = "The security groups used by the ECS web task" - value = tolist(module.ecs_deployment.web_network_configuration_security_groups)[0] - + description = "The security group used by the ECS web task" + value = tolist(module.ecs_deployment.web_network_configuration_security_groups)[0] } output "web_network_configuration_subnet" { @@ -85,3 +84,22 @@ output "web_task_definition_arn" { description = "The ARN of the ECS web service task definition" value = module.ecs_deployment.web_task_definition_arn } + +output "web_log_group_name" { + description = "The name of the cloudwatch log group for the web service task" + value = module.ecs_deployment.web_log_group_name +} + +output "worker_service_name" { + description = "The name of the ECS worker service. This is also the container name." + value = module.ecs_deployment.worker_service_name +} + +output "worker_task_desired_count" { + description = "The intended number of worker tasks" + value = module.ecs_deployment.worker_task_desired_count +} + +output "worker_log_group_name" { + description = "The name of the cloudwatch log group for the web service task" + value = module.ecs_deployment.worker_log_group_name diff --git a/terraform/envs/staging/main.tf b/terraform/envs/staging/main.tf index ec8ba2b..a82571f 100644 --- a/terraform/envs/staging/main.tf +++ b/terraform/envs/staging/main.tf @@ -41,7 +41,7 @@ module "ecs_deployment" { rds_multi_az = false container_web_cpu = 256 container_web_memory = 1024 - container_count = 1 + container_web_count = 1 ssl_policy = "ELBSecurityPolicy-TLS13-1-2-Res-FIPS-2023-04" } @@ -50,9 +50,9 @@ output "cluster_id" { value = module.ecs_deployment.cluster_id } -output "cloudwatch_log_group_name" { - description = "The name of the cloudwatch log group for the web service task" - value = module.ecs_deployment.cloudwatch_log_group_name +output "ecr_image_uri" { + description = "ECR URI where the environment's image is stored" + value = module.ecs_deployment.ecr_image_uri } output "ecr_repository_name" { @@ -66,14 +66,13 @@ output "public_ip" { } output "web_service_name" { - description = "The name of the ECS container running the web service" + description = "The name of the ECS web service. This is also the container name." value = module.ecs_deployment.web_service_name } output "web_network_configuration_security_group" { - description = "The security groups used by the ECS web task" - value = tolist(module.ecs_deployment.web_network_configuration_security_groups)[0] - + description = "The security group used by the ECS web task" + value = tolist(module.ecs_deployment.web_network_configuration_security_groups)[0] } output "web_network_configuration_subnet" { @@ -85,3 +84,22 @@ output "web_task_definition_arn" { description = "The ARN of the ECS web service task definition" value = module.ecs_deployment.web_task_definition_arn } + +output "web_log_group_name" { + description = "The name of the cloudwatch log group for the web service task" + value = module.ecs_deployment.web_log_group_name +} + +output "worker_service_name" { + description = "The name of the ECS worker service. This is also the container name." + value = module.ecs_deployment.worker_service_name +} + +output "worker_task_desired_count" { + description = "The intended number of worker tasks" + value = module.ecs_deployment.worker_task_desired_count +} + +output "worker_log_group_name" { + description = "The name of the cloudwatch log group for the web service task" + value = module.ecs_deployment.worker_log_group_name diff --git a/terraform/modules/ecs_deployment/ecs.tf b/terraform/modules/ecs_deployment/ecs.tf index 653949c..2fe0260 100644 --- a/terraform/modules/ecs_deployment/ecs.tf +++ b/terraform/modules/ecs_deployment/ecs.tf @@ -51,7 +51,7 @@ resource "aws_ecs_service" "web" { name = "${local.app_env_name}-web" cluster = aws_ecs_cluster.cluster.id task_definition = aws_ecs_task_definition.web.arn - desired_count = var.container_count + desired_count = var.container_web_count launch_type = "FARGATE" enable_execute_command = true @@ -76,4 +76,60 @@ resource "aws_ecs_service" "web" { resource "aws_cloudwatch_log_group" "web_log_group" { name = "${local.app_env_name}-web" retention_in_days = 90 +} + + +resource "aws_ecs_task_definition" "worker" { + family = "${local.app_env_name}-worker" + + container_definitions = jsonencode([ + { + name = "${local.app_env_name}-worker" + image = local.ecr_image_uri + essential = true + logConfiguration = { + logDriver = "awslogs", + options = { + awslogs-group = aws_cloudwatch_log_group.worker_log_group.name, + awslogs-region = data.aws_region.current.name, + awslogs-stream-prefix = "ecs" + } + } + secrets = local.ecs_secrets + command = local.ecs_worker_command + } + ]) + + requires_compatibilities = ["FARGATE"] + cpu = var.container_worker_cpu + memory = var.container_worker_memory + execution_role_arn = aws_iam_role.ecs_execution_role.arn + network_mode = "awsvpc" + task_role_arn = aws_iam_role.ecs_task_role.arn +} + +resource "aws_ecs_service" "worker" { + name = "${local.app_env_name}-worker" + + cluster = aws_ecs_cluster.cluster.id + task_definition = aws_ecs_task_definition.worker.arn + desired_count = var.container_worker_count + launch_type = "FARGATE" + enable_execute_command = true + + deployment_circuit_breaker { + enable = true + rollback = true + } + + network_configuration { + subnets = data.aws_subnets.subnets.ids + security_groups = [aws_security_group.worker.id] + assign_public_ip = false + } +} + +resource "aws_cloudwatch_log_group" "worker_log_group" { + name = "${local.app_env_name}-worker" + retention_in_days = 90 } \ No newline at end of file diff --git a/terraform/modules/ecs_deployment/elasticache.tf b/terraform/modules/ecs_deployment/elasticache.tf new file mode 100644 index 0000000..409c40a --- /dev/null +++ b/terraform/modules/ecs_deployment/elasticache.tf @@ -0,0 +1,31 @@ +resource "aws_elasticache_replication_group" "redis" { + replication_group_id = "${local.app_env_name}-redis" + description = "Redis replication group" + apply_immediately = true + auto_minor_version_upgrade = true + automatic_failover_enabled = true + engine_version = "7.1" + node_type = var.redis_instance_type + num_cache_clusters = 2 + port = 6379 + preferred_cache_cluster_azs = ["us-east-1a", "us-east-1b"] + security_group_ids = [aws_security_group.redis.id] + subnet_group_name = aws_elasticache_subnet_group.redis.name + + log_delivery_configuration { + destination = aws_cloudwatch_log_group.redis_log_group.name + destination_type = "cloudwatch-logs" + log_format = "text" + log_type = "engine-log" + } +} + +resource "aws_elasticache_subnet_group" "redis" { + name = "${local.app_env_name}-redis-subnets" + subnet_ids = data.aws_subnets.subnets.ids +} + +resource "aws_cloudwatch_log_group" "redis_log_group" { + name = "${local.app_env_name}-redis" + retention_in_days = 90 +} \ No newline at end of file diff --git a/terraform/modules/ecs_deployment/iam.tf b/terraform/modules/ecs_deployment/iam.tf index 54bb2cf..000cae5 100644 --- a/terraform/modules/ecs_deployment/iam.tf +++ b/terraform/modules/ecs_deployment/iam.tf @@ -38,7 +38,8 @@ data "aws_iam_policy_document" "ecs_execution_role_policy" { "logs:PutLogEvents" ] resources = [ - "${aws_cloudwatch_log_group.web_log_group.arn}:*" + "${aws_cloudwatch_log_group.web_log_group.arn}:*", + "${aws_cloudwatch_log_group.worker_log_group.arn}:*", ] } diff --git a/terraform/modules/ecs_deployment/locals.tf b/terraform/modules/ecs_deployment/locals.tf index 81d49a1..b9a9df9 100644 --- a/terraform/modules/ecs_deployment/locals.tf +++ b/terraform/modules/ecs_deployment/locals.tf @@ -21,5 +21,8 @@ locals { } ] + # If different web and worker config is needed, split this into web- and worker- ecs_secrets = concat(local.ecs_infrastructure_secrets, local.ecs_config_secrets) + + ecs_worker_command = split(" ", "celery -A config worker --beat --scheduler redbeat.RedBeatScheduler --loglevel=INFO") } \ No newline at end of file diff --git a/terraform/modules/ecs_deployment/outputs.tf b/terraform/modules/ecs_deployment/outputs.tf index 8d77caa..9f2ce2d 100644 --- a/terraform/modules/ecs_deployment/outputs.tf +++ b/terraform/modules/ecs_deployment/outputs.tf @@ -3,11 +3,6 @@ output "cluster_id" { value = aws_ecs_cluster.cluster.id } -output "cloudwatch_log_group_name" { - description = "The name of the cloudwatch log group for the web service task" - value = aws_cloudwatch_log_group.web_log_group.name -} - output "ecr_image_uri" { description = "The full URI of the ECR image" value = local.ecr_image_uri @@ -34,11 +29,31 @@ output "web_network_configuration_security_groups" { } output "web_network_configuration_subnets" { - description = "The ID of the subnets used by the web task" + description = "The ID of one of the subnets used by the web task" value = aws_ecs_service.web.network_configuration[0].subnets } output "web_task_definition_arn" { description = "The ARN of the ECS web service task definition" value = aws_ecs_task_definition.web.arn +} + +output "web_log_group_name" { + description = "The name of the cloudwatch log group for the web service task" + value = aws_cloudwatch_log_group.web_log_group.name +} + +output "worker_service_name" { + description = "The name of the ECS worker service. This is also the container name." + value = aws_ecs_service.worker.name +} + +output "worker_task_desired_count" { + description = "The intended number of worker tasks" + value = aws_ecs_service.worker.desired_count +} + +output "worker_log_group_name" { + description = "The name of the cloudwatch log group for the worker service task" + value = aws_cloudwatch_log_group.worker_log_group.name } \ No newline at end of file diff --git a/terraform/modules/ecs_deployment/secrets_manager.tf b/terraform/modules/ecs_deployment/secrets_manager.tf index 5704235..fdaee83 100644 --- a/terraform/modules/ecs_deployment/secrets_manager.tf +++ b/terraform/modules/ecs_deployment/secrets_manager.tf @@ -13,6 +13,7 @@ resource "aws_secretsmanager_secret_version" "web_infrastructure" { ) DEFAULT_FROM_EMAIL = var.ses_from_email SECRET_KEY = random_password.app_secret_key.result + CELERY_BROKER_URL = "redis://${aws_elasticache_replication_group.redis.primary_endpoint_address}:${aws_elasticache_replication_group.redis.port}" }) } diff --git a/terraform/modules/ecs_deployment/security_groups.tf b/terraform/modules/ecs_deployment/security_groups.tf index fac1c38..c012a79 100644 --- a/terraform/modules/ecs_deployment/security_groups.tf +++ b/terraform/modules/ecs_deployment/security_groups.tf @@ -58,6 +58,25 @@ resource "aws_security_group" "web" { } } +resource "aws_security_group" "worker" { + name = "${local.app_env_name}-worker" + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.application_name} ${var.environment_name} worker" + } + + lifecycle { + create_before_destroy = true + } +} + resource "aws_security_group" "database" { name = "${local.app_env_name}-db" @@ -65,7 +84,7 @@ resource "aws_security_group" "database" { from_port = 5432 to_port = 5432 protocol = "tcp" - security_groups = [aws_security_group.web.id] + security_groups = [aws_security_group.web.id, aws_security_group.worker.id] } egress { @@ -83,3 +102,29 @@ resource "aws_security_group" "database" { create_before_destroy = true } } + +resource "aws_security_group" "redis" { + name = "${local.app_env_name}-redis" + + ingress { + from_port = 6379 + to_port = 6379 + protocol = "tcp" + security_groups = [aws_security_group.web.id, aws_security_group.worker.id] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.application_name} ${var.environment_name} redis" + } + + lifecycle { + create_before_destroy = true + } +} diff --git a/terraform/modules/ecs_deployment/variables.tf b/terraform/modules/ecs_deployment/variables.tf index 0369bac..6407470 100644 --- a/terraform/modules/ecs_deployment/variables.tf +++ b/terraform/modules/ecs_deployment/variables.tf @@ -70,7 +70,22 @@ variable "container_web_memory" { default = 1024 } -variable "container_count" { +variable "container_web_count" { + type = number + default = 1 +} + +variable "container_worker_cpu" { + type = number + default = 256 +} + +variable "container_worker_memory" { + type = number + default = 1024 +} + +variable "container_worker_count" { type = number default = 1 } @@ -79,3 +94,8 @@ variable "ssl_policy" { type = string default = "ELBSecurityPolicy-TLS13-1-2-Res-FIPS-2023-04" } + +variable "redis_instance_type" { + type = string + default = "cache.t3.micro" +} From 0c48ab54142a074c67916ffcdbbef3cb4498cb82 Mon Sep 17 00:00:00 2001 From: Grace Whitney Date: Fri, 13 Dec 2024 15:57:02 -0500 Subject: [PATCH 2/3] Update readme --- readme.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index bce8b85..bb95bfd 100644 --- a/readme.md +++ b/readme.md @@ -123,6 +123,8 @@ Following that, deploy your code to the environment (see below). ## Creating a new ECS environment +The terraform configuration for ECS deployments will create both a web and worker environment, +with a Redis instance to act as a task broker. 1. Create an ECR repository 2. Build and push an initial docker file to it (ECR provides docker commands for this). @@ -138,7 +140,6 @@ terraform init terraform plan terraform apply ``` - 9. Redeploy your code using the steps described below (with the --use-latest option) to run initial migrations 10. Add a DNS entry from your domain name to the created load balancer @@ -159,8 +160,9 @@ python deploy.py -env This script will do the following: 1. Build the docker image using your local code version. 2. Push the docker image to the ECR location for the specified environment -3. Run database migrations -4. Deploy to the running web service +3. Stop the running worker service +4. Run database migrations +5. Deploy to the running web service and restart the worker service Run `python deploy.py --help` to see available options. You may choose to use an existing ECR image or skip migrations. From ae3ba3d576f3ddcffecb6b58516d255dafafe5fe Mon Sep 17 00:00:00 2001 From: Grace Whitney Date: Fri, 13 Dec 2024 16:13:14 -0500 Subject: [PATCH 3/3] Add celery config to code --- common/tasks.py | 40 ++++++++++++++++++++++++++++++++++++++++ config/celery.py | 28 ++++++++++++++++++++++++++++ config/settings.py | 7 +++++++ readme.md | 2 +- requirements.in | 2 ++ 5 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 common/tasks.py create mode 100644 config/celery.py diff --git a/common/tasks.py b/common/tasks.py new file mode 100644 index 0000000..37765d2 --- /dev/null +++ b/common/tasks.py @@ -0,0 +1,40 @@ +import logging +from datetime import timedelta + +import celery.schedules +from django.conf import settings +from django.db.models import TextChoices + +from config.celery import app + +class TaskFrequency(TextChoices): + five_minutes = "five_minutes" + daily = "daily" + + +SCHEDULES = { + TaskFrequency.five_minutes: celery.schedules.schedule(run_every=timedelta(minutes=5)), + TaskFrequency.daily: celery.schedules.crontab(minute=0, hour=7), +} + +# Set up scheduled tasks +@app.on_after_finalize.connect +def schedule_tasks(sender, **kwargs): + if settings.CELERY_TASK_ALWAYS_EAGER: + return + for task_frequency, tasks in SCHEDULED_TASKS.items(): + schedule = SCHEDULES[task_frequency] + for task_name, task in tasks.items(): + sender.add_periodic_task(schedule, task.s(), name=task_name) + + +# TODO: Delete me +@app.task +def sample_task(): + logging.info("Sample task running") + + +SCHEDULED_TASKS = { + TaskFrequency.five_minutes: {sample_task}, + TaskFrequency.daily: {}, +} diff --git a/config/celery.py b/config/celery.py new file mode 100644 index 0000000..5e8bf66 --- /dev/null +++ b/config/celery.py @@ -0,0 +1,28 @@ +import os + +from celery import Celery +from kombu.utils.json import register_type +from django.db.models import Model +from django.apps import apps + +# Allow serialization of Django models +register_type( + Model, + "model", + lambda o: [o._meta.label, o.pk], + lambda o: apps.get_model(o[0]).objects.get(pk=o[1]), +) + +# Set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + +app = Celery("sample-django-app") + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object('django.conf:settings', namespace='CELERY') + +# Load task modules from all registered Django apps. +app.autodiscover_tasks() \ No newline at end of file diff --git a/config/settings.py b/config/settings.py index e311c35..18eb24c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -422,3 +422,10 @@ ] COMPRESS_ROOT = STORAGES["sass_processor"]["ROOT"] # END_FEATURE sass_bootstrap + + +# Celery configuration +CELERY_TIMEZONE = TIME_ZONE +CELERY_BROKER_URL = env("CELERY_BROKER_URL", default="") +# If no broker URL configured, run tasks in the web process +CELERY_TASK_ALWAYS_EAGER = not CELERY_BROKER_URL diff --git a/readme.md b/readme.md index bb95bfd..ddde227 100644 --- a/readme.md +++ b/readme.md @@ -131,7 +131,7 @@ with a Redis instance to act as a task broker. 3. Create a bucket for holding terraform config 4. Create an SES identity and from email (if using SES) 5. Create an AWS certificate manager certificate for your domain -6. Create a secrets manager secret containing the config parameters needed by the application (you do not need include "DATABASE_URL", "SECRET_KEY", "AWS_STORAGE_BUCKET_NAME", or "DEFAULT_FROM_EMAIL" as those are managed by terraform in `terraform/modules/ecs_deployment/secrets_manager.tf`) +6. Create a secrets manager secret containing the config parameters needed by the application (you do not need include "DATABASE_URL", "SECRET_KEY", "AWS_STORAGE_BUCKET_NAME", "DEFAULT_FROM_EMAIL", or "CELERY_BROKER_URL" as those are managed by terraform in `terraform/modules/ecs_deployment/secrets_manager.tf`) 7. Fill in the missing values in `terraform/envs//main.tf` 8. Run terraform to set up that environment ``` diff --git a/requirements.in b/requirements.in index e7ad4e9..d3a7cbf 100644 --- a/requirements.in +++ b/requirements.in @@ -34,6 +34,8 @@ django-storages # START_FEATURE docker gunicorn whitenoise +celery[redis] +celery-redbeat # END_FEATURE docker # START_FEATURE django_ses