From 9ea7654b484873c7e0d90e3790b0f1ad1d305796 Mon Sep 17 00:00:00 2001 From: Marco Acierno Date: Mon, 9 Sep 2024 00:13:22 +0200 Subject: [PATCH] ee --- backend/Dockerfile | 2 +- .../applications/pretix_arm/task_web.tf | 10 + .../applications/pretix_arm/task_worker.tf | 213 +++++++----------- .../applications/pycon_backend/main.tf | 14 +- .../applications/pycon_backend/providers.tf | 1 - .../applications/pycon_backend/task_web.tf | 4 +- .../{worker.tf => task_worker.tf} | 16 +- .../pycon_backend/worker_heavy_processing.tf | 12 +- .../applications/server/cloudfront.tf | 4 +- infrastructure/applications/server/db.tf | 2 +- infrastructure/applications/server/ecs.tf | 2 +- infrastructure/applications/server/main.tf | 36 +-- .../applications/server/security.tf | 9 - .../applications/server/task_traefik.tf | 8 +- pretix/settings.py | 10 +- 15 files changed, 152 insertions(+), 191 deletions(-) rename infrastructure/applications/pycon_backend/{worker.tf => task_worker.tf} (97%) diff --git a/backend/Dockerfile b/backend/Dockerfile index 8de8cb6d9d..fbb620be7e 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -13,7 +13,7 @@ RUN apt-get update -y && apt-get install -y \ libtiff5-dev libjpeg62 libopenjp2-7-dev zlib1g-dev \ libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk \ libharfbuzz-dev libfribidi-dev libxcb1-dev libldap2-dev libldap-2.5-0 \ - ffmpeg libsm6 libxext6 libglib2.0-0 + ffmpeg libsm6 libxext6 libglib2.0-0 curl ENV LIBRARY_PATH=/lib:/usr/lib diff --git a/infrastructure/applications/pretix_arm/task_web.tf b/infrastructure/applications/pretix_arm/task_web.tf index a569703ffe..216dc3ac1f 100644 --- a/infrastructure/applications/pretix_arm/task_web.tf +++ b/infrastructure/applications/pretix_arm/task_web.tf @@ -177,6 +177,16 @@ resource "aws_ecs_task_definition" "pretix_web" { "traefik.http.routers.pretix-web.rule" = "Host(`${local.domain}`)" } + healthCheck = { + retries = 3 + command = [ + "CMD-SHELL", + "curl -f http://localhost/healthcheck/ || exit 1" + ] + timeout = 3 + interval = 10 + } + systemControls = [ { "namespace" : "net.core.somaxconn", diff --git a/infrastructure/applications/pretix_arm/task_worker.tf b/infrastructure/applications/pretix_arm/task_worker.tf index 1847079956..177c2af5c5 100644 --- a/infrastructure/applications/pretix_arm/task_worker.tf +++ b/infrastructure/applications/pretix_arm/task_worker.tf @@ -1,131 +1,92 @@ -# resource "aws_ecs_task_definition" "pretix_web" { -# family = "pythonit-${terraform.workspace}-pretix" -# container_definitions = jsonencode([ -# { -# name = "pretix" -# image = "${data.aws_ecr_repository.repo.repository_url}@${data.aws_ecr_image.image.image_digest}" -# memoryReservation = 200 -# essential = true -# environment = [ -# { -# name = "DATABASE_NAME" -# value = "pretix" -# }, -# { -# name = "DATABASE_USERNAME" -# value = data.aws_db_instance.database.master_username -# }, -# { -# name = "DATABASE_PASSWORD" -# value = module.common_secrets.value.database_password -# }, -# { -# name = "DATABASE_HOST" -# value = data.aws_db_instance.database.address -# }, -# { -# name = "MAIL_USER" -# value = module.secrets.value.mail_user -# }, -# { -# name = "MAIL_PASSWORD" -# value = module.secrets.value.mail_password -# }, -# { -# name = "PRETIX_SENTRY_DSN" -# value = module.secrets.value.sentry_dsn -# }, -# { -# name = "SECRET_KEY" -# value = module.secrets.value.secret_key -# }, -# { -# name = "PRETIX_REDIS_LOCATION", -# value = "redis://${data.aws_instance.redis.private_ip}/0" -# }, -# { -# name = "PRETIX_REDIS_SESSIONS", -# value = "false" -# }, -# { -# name = "PRETIX_CELERY_BROKER", -# value = "redis://${data.aws_instance.redis.private_ip}/1" -# }, -# { -# name = "PRETIX_CELERY_BACKEND", -# value = "redis://${data.aws_instance.redis.private_ip}/2" -# }, -# { -# name = "PRETIX_PRETIX_URL", -# value = "https://tickets.pycon.it/" -# }, -# { -# name = "PRETIX_PRETIX_TRUST_X_FORWARDED_PROTO", -# value = "true" -# } -# ] -# portMappings = [ -# { -# containerPort = 80 -# hostPort = 0 -# } -# ] -# dockerLabels = { -# "traefik.enable" = "true" -# "traefik.http.routers.backend.rule" = "Host(`tickets.pycon.it`)" -# } -# mountPoints = [ -# { -# sourceVolume = "media" -# containerPath = "/data/media" -# }, -# { -# sourceVolume = "data" -# containerPath = "/var/pretix-data" -# } -# ] -# systemControls = [ -# { -# "namespace" : "net.core.somaxconn", -# "value" : "4096" -# } -# ] -# logConfiguration = { -# logDriver = "awslogs" -# options = { -# "awslogs-group" = aws_cloudwatch_log_group.pretix.name -# "awslogs-region" = "eu-central-1" -# "awslogs-stream-prefix" = "ecs" -# } -# } -# }, -# ]) +resource "aws_cloudwatch_log_group" "pretix_worker" { + name = "/ecs/pythonit-${terraform.workspace}-pretix-worker" + retention_in_days = 7 +} -# volume { -# name = "media" -# host_path = "/var/pretix/data/media" -# } +resource "aws_ecs_task_definition" "pretix_worker" { + family = "pythonit-${terraform.workspace}-pretix-worker" + container_definitions = jsonencode([ + { + name = "worker" + image = "${data.aws_ecr_repository.repo.repository_url}@${data.aws_ecr_image.image.image_digest}" + memoryReservation = 200 + essential = true + environment = local.env_vars -# volume { -# name = "data" -# host_path = "/var/pretix-data" -# } + entrypoint = ["pretix"] + command = ["taskworker"] -# requires_compatibilities = [] -# tags = {} -# } + workingDirectory = "/pretix/src" + user = "pretixuser" -# resource "aws_ecs_service" "pretix_web" { -# name = "pretix-worker" -# cluster = data.aws_ecs_cluster.server.id -# task_definition = aws_ecs_task_definition.pretix_web.arn -# desired_count = 1 -# deployment_minimum_healthy_percent = 100 -# deployment_maximum_percent = 200 + healthCheck = { + retries = 3 + command = [ + "CMD-SHELL", + "celery -A pretix.celery_app inspect ping" + ] + timeout = 3 + interval = 10 + } -# lifecycle { -# ignore_changes = [ -# capacity_provider_strategy -# ] -# } -# } + logConfiguration = { + logDriver = "awslogs" + options = { + "awslogs-group" = aws_cloudwatch_log_group.pretix_worker.name + "awslogs-region" = "eu-central-1" + "awslogs-stream-prefix" = "ecs" + } + } + }, + { + name = "cron" + image = "${data.aws_ecr_repository.repo.repository_url}@${data.aws_ecr_image.image.image_digest}" + memoryReservation = 200 + essential = true + environment = local.env_vars + + entrypoint = ["bash", "-c"] + command = ["while true; do pretix cron; sleep 60; done"] + + healthCheck = { + retries = 3 + command = [ + "CMD-SHELL", + "echo 1" + ] + timeout = 3 + interval = 10 + } + + workingDirectory = "/pretix/src" + user = "pretixuser" + + logConfiguration = { + logDriver = "awslogs" + options = { + "awslogs-group" = aws_cloudwatch_log_group.pretix_worker.name + "awslogs-region" = "eu-central-1" + "awslogs-stream-prefix" = "ecs" + } + } + }, + ]) + + requires_compatibilities = [] + tags = {} +} + +resource "aws_ecs_service" "pretix_worker" { + name = "pretix-worker" + cluster = data.aws_ecs_cluster.server.id + task_definition = aws_ecs_task_definition.pretix_worker.arn + desired_count = 1 + deployment_minimum_healthy_percent = 100 + deployment_maximum_percent = 200 + + lifecycle { + ignore_changes = [ + capacity_provider_strategy + ] + } +} diff --git a/infrastructure/applications/pycon_backend/main.tf b/infrastructure/applications/pycon_backend/main.tf index 3f4c3a9567..5485efe214 100644 --- a/infrastructure/applications/pycon_backend/main.tf +++ b/infrastructure/applications/pycon_backend/main.tf @@ -1,8 +1,8 @@ locals { - is_prod = terraform.workspace == "production" - db_connection = var.enable_proxy ? "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_proxy.proxy[0].endpoint}:${data.aws_db_instance.database.port}/pycon" : "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/pycon" - cdn_url = local.is_prod ? "cdn.pycon.it" : "${terraform.workspace}-cdn.pycon.it" - web_domain = local.is_prod ? "admin.pycon.it" : "${terraform.workspace}-admin.pycon.it" + is_prod = terraform.workspace == "production" + db_connection = var.enable_proxy ? "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_proxy.proxy[0].endpoint}:${data.aws_db_instance.database.port}/pycon" : "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/pycon" + cdn_url = local.is_prod ? "cdn.pycon.it" : "${terraform.workspace}-cdn.pycon.it" + web_domain = local.is_prod ? "admin.pycon.it" : "${terraform.workspace}-admin.pycon.it" } data "aws_vpc" "default" { @@ -113,8 +113,8 @@ module "lambda" { CELERY_RESULT_BACKEND = local.is_prod ? "redis://${data.aws_instance.redis.private_ip}/6" : "redis://${data.aws_instance.redis.private_ip}/15" PLAIN_INTEGRATION_TOKEN = module.secrets.value.plain_integration_token HASHID_DEFAULT_SECRET_SALT = module.secrets.value.hashid_default_secret_salt - MEDIA_FILES_STORAGE_BACKEND = "pycon.storages.CustomS3Boto3Storage" - SNS_WEBHOOK_SECRET = module.common_secrets.value.sns_webhook_secret - AWS_SES_CONFIGURATION_SET = data.aws_sesv2_configuration_set.main.configuration_set_name + MEDIA_FILES_STORAGE_BACKEND = "pycon.storages.CustomS3Boto3Storage" + SNS_WEBHOOK_SECRET = module.common_secrets.value.sns_webhook_secret + AWS_SES_CONFIGURATION_SET = data.aws_sesv2_configuration_set.main.configuration_set_name } } diff --git a/infrastructure/applications/pycon_backend/providers.tf b/infrastructure/applications/pycon_backend/providers.tf index 53481f218f..2ec15ed060 100644 --- a/infrastructure/applications/pycon_backend/providers.tf +++ b/infrastructure/applications/pycon_backend/providers.tf @@ -2,7 +2,6 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "5.66.0" configuration_aliases = [aws.us] } } diff --git a/infrastructure/applications/pycon_backend/task_web.tf b/infrastructure/applications/pycon_backend/task_web.tf index 258d3e2748..3dbc15544f 100644 --- a/infrastructure/applications/pycon_backend/task_web.tf +++ b/infrastructure/applications/pycon_backend/task_web.tf @@ -25,7 +25,7 @@ resource "aws_ecs_task_definition" "backend" { ] dockerLabels = { - "traefik.enable" = "true" + "traefik.enable" = "true" "traefik.http.routers.backend-web.rule" = "Host(`${local.web_domain}`)" } @@ -58,7 +58,7 @@ resource "aws_ecs_task_definition" "backend" { retries = 3 command = [ "CMD-SHELL", - "echo 1" + "curl -f http://localhost:8000/health/ || exit 1" ] timeout = 3 interval = 10 diff --git a/infrastructure/applications/pycon_backend/worker.tf b/infrastructure/applications/pycon_backend/task_worker.tf similarity index 97% rename from infrastructure/applications/pycon_backend/worker.tf rename to infrastructure/applications/pycon_backend/task_worker.tf index b75e07e988..ebe1966df8 100644 --- a/infrastructure/applications/pycon_backend/worker.tf +++ b/infrastructure/applications/pycon_backend/task_worker.tf @@ -173,11 +173,11 @@ locals { value = module.secrets.value.hashid_default_secret_salt }, { - name = "MEDIA_FILES_STORAGE_BACKEND", + name = "MEDIA_FILES_STORAGE_BACKEND", value = "pycon.storages.CustomS3Boto3Storage" }, { - name = "CLAMAV_HOST", + name = "CLAMAV_HOST", value = module.secrets.value.clamav_host }, { @@ -192,15 +192,15 @@ locals { }) }, { - name = "ECS_SERVICE_ROLE", + name = "ECS_SERVICE_ROLE", value = aws_iam_role.ecs_service.arn }, { - name = "AWS_SES_CONFIGURATION_SET" + name = "AWS_SES_CONFIGURATION_SET" value = data.aws_sesv2_configuration_set.main.configuration_set_name }, { - name = "SNS_WEBHOOK_SECRET" + name = "SNS_WEBHOOK_SECRET" value = module.common_secrets.value.sns_webhook_secret } ] @@ -291,8 +291,8 @@ resource "aws_instance" "instance_1" { market_type = "spot" spot_options { - max_price = 0.0031 - spot_instance_type = "persistent" + max_price = 0.0031 + spot_instance_type = "persistent" instance_interruption_behavior = "stop" } } @@ -352,7 +352,7 @@ resource "aws_ecs_task_definition" "worker" { retries = 3 command = [ "CMD-SHELL", - "echo 1" + "celery -A pycon inspect ping" ] timeout = 3 interval = 10 diff --git a/infrastructure/applications/pycon_backend/worker_heavy_processing.tf b/infrastructure/applications/pycon_backend/worker_heavy_processing.tf index 5f1561219f..70f28bd250 100644 --- a/infrastructure/applications/pycon_backend/worker_heavy_processing.tf +++ b/infrastructure/applications/pycon_backend/worker_heavy_processing.tf @@ -13,20 +13,20 @@ resource "aws_cloudwatch_log_group" "heavy_processing_worker_logs" { } resource "aws_ecs_task_definition" "heavy_processing_worker" { - family = "pythonit-${terraform.workspace}-heavy-processing-worker" + family = "pythonit-${terraform.workspace}-heavy-processing-worker" requires_compatibilities = ["FARGATE"] cpu = 4096 memory = 16384 network_mode = "awsvpc" - execution_role_arn = aws_iam_role.worker.arn - task_role_arn = aws_iam_role.worker.arn + execution_role_arn = aws_iam_role.worker.arn + task_role_arn = aws_iam_role.worker.arn ephemeral_storage { size_in_gib = 21 } runtime_platform { operating_system_family = "LINUX" - cpu_architecture = "ARM64" + cpu_architecture = "ARM64" } container_definitions = jsonencode([ { @@ -83,9 +83,9 @@ resource "aws_ecs_task_definition" "heavy_processing_worker" { ]) volume { - name = "storage" + name = "storage" configure_at_launch = true } - tags = {} + tags = {} } diff --git a/infrastructure/applications/server/cloudfront.tf b/infrastructure/applications/server/cloudfront.tf index 9e24120b03..557ffb89fc 100644 --- a/infrastructure/applications/server/cloudfront.tf +++ b/infrastructure/applications/server/cloudfront.tf @@ -1,5 +1,5 @@ locals { - pycon_web_domain = local.is_prod ? "admin.pycon.it" : "${terraform.workspace}-admin.pycon.it" + pycon_web_domain = local.is_prod ? "admin.pycon.it" : "${terraform.workspace}-admin.pycon.it" pretix_web_domain = local.is_prod ? "tickets.pycon.it" : "${terraform.workspace}-tickets.pycon.it" } @@ -33,7 +33,7 @@ resource "aws_cloudfront_distribution" "application" { is_ipv6_enabled = true comment = "${terraform.workspace} server" wait_for_deployment = false - aliases = [ + aliases = [ local.pycon_web_domain, local.pretix_web_domain ] diff --git a/infrastructure/applications/server/db.tf b/infrastructure/applications/server/db.tf index 654129144f..b8086bf5e1 100644 --- a/infrastructure/applications/server/db.tf +++ b/infrastructure/applications/server/db.tf @@ -1,5 +1,5 @@ locals { - db_connection = "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/pycon" + db_connection = "postgres://${data.aws_db_instance.database.master_username}:${module.common_secrets.value.database_password}@${data.aws_db_instance.database.address}:${data.aws_db_instance.database.port}/pycon" } data "aws_db_instance" "database" { diff --git a/infrastructure/applications/server/ecs.tf b/infrastructure/applications/server/ecs.tf index 8ee260eaab..2cc3058d7b 100644 --- a/infrastructure/applications/server/ecs.tf +++ b/infrastructure/applications/server/ecs.tf @@ -14,7 +14,7 @@ resource "aws_ecs_capacity_provider" "server" { minimum_scaling_step_size = 1 status = "ENABLED" target_capacity = 1 - instance_warmup_period = 60 + instance_warmup_period = 60 } } } diff --git a/infrastructure/applications/server/main.tf b/infrastructure/applications/server/main.tf index c57170be49..7e0b2c013b 100644 --- a/infrastructure/applications/server/main.tf +++ b/infrastructure/applications/server/main.tf @@ -6,7 +6,7 @@ data "template_file" "server_user_data" { } data "aws_ami" "ecs" { - owners = ["self"] + owners = ["self"] filter { name = "image-id" @@ -15,15 +15,15 @@ data "aws_ami" "ecs" { } data "aws_security_group" "tempone" { - name = "pythonit-${terraform.workspace}-worker-instance" + name = "pythonit-${terraform.workspace}-worker-instance" } resource "aws_launch_template" "server" { - name = "pythonit-${terraform.workspace}-server" + name = "pythonit-${terraform.workspace}-server" image_id = var.ecs_arm_ami instance_type = "t4g.medium" - user_data = base64encode(data.template_file.server_user_data.rendered) - key_name = "pretix" + user_data = base64encode(data.template_file.server_user_data.rendered) + key_name = "pretix" iam_instance_profile { name = aws_iam_instance_profile.server.name @@ -41,30 +41,30 @@ resource "aws_launch_template" "server" { associate_public_ip_address = true security_groups = [ data.aws_security_group.rds.id, - data.aws_security_group.lambda.id, - data.aws_security_group.tempone.id, - aws_security_group.server.id, + data.aws_security_group.lambda.id, + data.aws_security_group.tempone.id, + aws_security_group.server.id, ] subnet_id = data.aws_subnet.public_1a.id } } resource "aws_autoscaling_group" "server" { - name = "pythonit-${terraform.workspace}-server" - vpc_zone_identifier = [data.aws_subnet.public_1a.id] - desired_capacity = 1 - max_size = 1 - min_size = 1 - termination_policies = ["OldestInstance"] + name = "pythonit-${terraform.workspace}-server" + vpc_zone_identifier = [data.aws_subnet.public_1a.id] + desired_capacity = 1 + max_size = 1 + min_size = 1 + termination_policies = ["OldestInstance"] protect_from_scale_in = true instance_refresh { strategy = "Rolling" preferences { - min_healthy_percentage = 100 - max_healthy_percentage = 110 + min_healthy_percentage = 100 + max_healthy_percentage = 110 scale_in_protected_instances = "Refresh" - instance_warmup = 30 + instance_warmup = 30 } } @@ -74,7 +74,7 @@ resource "aws_autoscaling_group" "server" { } tag { - key = "Name" + key = "Name" value = "pythonit-${terraform.workspace}-server" propagate_at_launch = true } diff --git a/infrastructure/applications/server/security.tf b/infrastructure/applications/server/security.tf index ae2fb1b588..8b8b953803 100644 --- a/infrastructure/applications/server/security.tf +++ b/infrastructure/applications/server/security.tf @@ -29,12 +29,3 @@ resource "aws_security_group_rule" "web_http" { cidr_blocks = ["0.0.0.0/0"] security_group_id = aws_security_group.server.id } - -resource "aws_security_group_rule" "web_dashboard" { - type = "ingress" - from_port = 8080 - to_port = 8080 - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] - security_group_id = aws_security_group.server.id -} diff --git a/infrastructure/applications/server/task_traefik.tf b/infrastructure/applications/server/task_traefik.tf index dcda123bd5..4ea05f61dc 100644 --- a/infrastructure/applications/server/task_traefik.tf +++ b/infrastructure/applications/server/task_traefik.tf @@ -14,19 +14,19 @@ resource "aws_ecs_task_definition" "traefik" { environment = [ { - name = "TRAEFIK_PROVIDERS_ECS_CLUSTERS" + name = "TRAEFIK_PROVIDERS_ECS_CLUSTERS" value = aws_ecs_cluster.server.name }, { - name = "TRAEFIK_PROVIDERS_ECS_AUTODISCOVERCLUSTERS" + name = "TRAEFIK_PROVIDERS_ECS_AUTODISCOVERCLUSTERS" value = "false", }, { - name = "TRAEFIK_PROVIDERS_ECS_EXPOSEDBYDEFAULT", + name = "TRAEFIK_PROVIDERS_ECS_EXPOSEDBYDEFAULT", value = "false", }, { - name = "TRAEFIK_ENTRYPOINTS_WEB_ADDRESS", + name = "TRAEFIK_ENTRYPOINTS_WEB_ADDRESS", value = ":80" }, ] diff --git a/pretix/settings.py b/pretix/settings.py index d730c6aafa..c3315213ff 100644 --- a/pretix/settings.py +++ b/pretix/settings.py @@ -1,17 +1,17 @@ from pretix.settings import * # noqa from pretix.settings import INSTALLED_APPS, ALL_LANGUAGES, LOGGING, STORAGES, config -LOGGING["handlers"]["mail_admins"]["include_html"] = True # noqa +LOGGING["handlers"]["mail_admins"]["include_html"] = True # Allow all the languages # see: pretix/settings.py#L425-L435 -LANGUAGES = [(k, v) for k, v in ALL_LANGUAGES] # noqa +LANGUAGES = [(k, v) for k, v in ALL_LANGUAGES] EMAIL_SUBJECT_PREFIX = "[PyCon Tickets] " -if "pretix_fattura_elettronica" in INSTALLED_APPS: # noqa - INSTALLED_APPS.remove("pretix_fattura_elettronica") # noqa - INSTALLED_APPS.insert(0, "pretix_fattura_elettronica") # noqa +if "pretix_fattura_elettronica" in INSTALLED_APPS: + INSTALLED_APPS.remove("pretix_fattura_elettronica") + INSTALLED_APPS.insert(0, "pretix_fattura_elettronica") STORAGES["default"]["BACKEND"] = "storages.backends.s3.S3Storage" AWS_STORAGE_BUCKET_NAME = config.get("pycon", "storage_bucket_name", fallback="")