Skip to content

Commit 529df60

Browse files
authored
Added Single-Instance Invite Bot ECS Service (#1)
* feat: add invite_bot ecs service * docs: update architecture diagram
1 parent f586b8c commit 529df60

9 files changed

Lines changed: 238 additions & 2 deletions

File tree

assets/architecture.drawio

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<mxfile host="Electron">
22
<diagram name="페이지-1" id="0xCIYineUgVJ7Auf2_G3">
3-
<mxGraphModel dx="972" dy="740" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="3300" pageHeight="4681" math="0" shadow="0">
3+
<mxGraphModel dx="1169" dy="836" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="3300" pageHeight="4681" math="0" shadow="0">
44
<root>
55
<mxCell id="0" />
66
<mxCell id="1" parent="0" />
@@ -224,6 +224,9 @@
224224
<mxCell id="dFDLIWHxTieYANeZmimu-5" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;fontSize=10;" value="(ASG : Optional)" vertex="1">
225225
<mxGeometry height="30" width="120" x="617.5" y="201" as="geometry" />
226226
</mxCell>
227+
<mxCell id="p5icJqS1yuqV0pEGQa0l-1" parent="1" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=11;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ecs;labelBackgroundColor=default;" value="ECS Fargate&lt;div&gt;Invite Bot&lt;/div&gt;" vertex="1">
228+
<mxGeometry height="30" width="30" x="560" y="380" as="geometry" />
229+
</mxCell>
227230
</root>
228231
</mxGraphModel>
229232
</diagram>

assets/architecture.png

11.2 KB
Loading

example.tfvars

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ backend_health_check_interval_seconds = 20
3131
backend_health_check_timeout_seconds = 5
3232
backend_health_check_healthy_threshold = 2
3333
backend_health_check_unhealthy_threshold = 2
34+
35+
# Invite bot (Discord). Deployed as a single ECS task (no sharding); see
36+
# modules/ecs/invite_bot.tf. When invite_bot_enabled = true, the backend's
37+
# DISCORD_BOT_BASE_URL is auto-set to the invite-bot Cloud Map DNS name.
38+
invite_bot_enabled = false
39+
invite_bot_image = "123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/invite-bot:latest"
40+
invite_bot_cpu = 256
41+
invite_bot_memory = 512
42+
invite_bot_environment = {
43+
DISCORD_BOT_TOKEN = ""
44+
DISCORD_GUILD_ID = ""
45+
DISCORD_VERIFIED_ROLE_ID = ""
46+
DISCORD_INTERNAL_SECRET = "change-me"
47+
}
3448
backend_environment = {
3549
APP_ENV = "local"
3650
HTTP_ADDR = ":8080"
@@ -81,9 +95,24 @@ backend_environment = {
8195
VMS_MAX_SCOPE = "team"
8296
VMS_MAX_PER = "2"
8397
VMS_ORCHESTRATOR_BASE_URL = "http://10.10.0.1:8082"
98+
VMS_ORCHESTRATOR_SECRET = "change-me"
8499
VMS_ORCHESTRATOR_TIMEOUT = "5s"
85100
VMS_CREATE_WINDOW = "1m"
86101
VMS_CREATE_MAX = "1"
102+
103+
DISCORD_ENABLED = "false"
104+
DISCORD_CLIENT_ID = ""
105+
DISCORD_CLIENT_SECRET = "change-me"
106+
DISCORD_REDIRECT_URI = "https://api.example.com/api/discord/callback"
107+
DISCORD_OAUTH_SCOPES = "identify guilds.join"
108+
DISCORD_STATE_TTL = "5m"
109+
DISCORD_OAUTH_TIMEOUT = "10s"
110+
DISCORD_SUCCESS_REDIRECT = "https://ctf.example.com/profile"
111+
DISCORD_INVITE_URL = ""
112+
DISCORD_AUTO_JOIN = "true"
113+
DISCORD_BOT_BASE_URL = "http://10.10.0.1:8083"
114+
DISCORD_BOT_SECRET = "change-me"
115+
DISCORD_BOT_TIMEOUT = "5s"
87116
}
88117
backend_log_retention_days = 14
89118

main.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ module "ecs" {
117117
backend_health_check_unhealthy_threshold = var.backend_health_check_unhealthy_threshold
118118
ecs_task_execution_role_arn = module.policy.ecs_task_execution_role_arn
119119
ecs_task_role_arn = module.policy.ecs_task_role_arn
120+
121+
invite_bot_enabled = var.invite_bot_enabled
122+
invite_bot_image = var.invite_bot_image
123+
invite_bot_cpu = var.invite_bot_cpu
124+
invite_bot_memory = var.invite_bot_memory
125+
invite_bot_environment = var.invite_bot_environment
126+
invite_bot_log_retention_days = var.invite_bot_log_retention_days
120127
}
121128

122129
module "sbxcluster" {

modules/ecs/invite_bot.tf

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
locals {
2+
invite_bot_count = var.invite_bot_enabled ? 1 : 0
3+
invite_bot_container_name = "invite-bot"
4+
5+
backend_environment_effective = merge(
6+
var.backend_environment,
7+
var.invite_bot_enabled ? {
8+
DISCORD_BOT_BASE_URL = "http://${aws_service_discovery_service.invite_bot[0].name}.${aws_service_discovery_private_dns_namespace.internal[0].name}:8083"
9+
} : {}
10+
)
11+
}
12+
13+
resource "aws_service_discovery_private_dns_namespace" "internal" {
14+
count = local.invite_bot_count
15+
name = "${var.name_prefix}.internal"
16+
description = "Internal service discovery namespace for ${var.name_prefix}"
17+
vpc = var.vpc_id
18+
tags = var.tags
19+
}
20+
21+
resource "aws_service_discovery_service" "invite_bot" {
22+
count = local.invite_bot_count
23+
name = "invite-bot"
24+
25+
dns_config {
26+
namespace_id = aws_service_discovery_private_dns_namespace.internal[0].id
27+
28+
dns_records {
29+
type = "A"
30+
ttl = 10
31+
}
32+
33+
routing_policy = "MULTIVALUE"
34+
}
35+
36+
health_check_custom_config {}
37+
38+
tags = var.tags
39+
}
40+
41+
resource "aws_security_group" "invite_bot" {
42+
count = local.invite_bot_count
43+
name = "${var.name_prefix}-invite-bot"
44+
description = "Invite bot ECS service security group"
45+
vpc_id = var.vpc_id
46+
47+
ingress {
48+
description = "Internal API from backend"
49+
from_port = 8083
50+
to_port = 8083
51+
protocol = "tcp"
52+
security_groups = [aws_security_group.backend.id]
53+
}
54+
55+
egress {
56+
from_port = 0
57+
to_port = 0
58+
protocol = "-1"
59+
cidr_blocks = ["0.0.0.0/0"]
60+
}
61+
62+
tags = merge(var.tags, { Name = "${var.name_prefix}-invite-bot" })
63+
}
64+
65+
resource "aws_cloudwatch_log_group" "invite_bot" {
66+
count = local.invite_bot_count
67+
name = "/aws/ecs/${var.name_prefix}/invite-bot"
68+
retention_in_days = var.invite_bot_log_retention_days
69+
tags = var.tags
70+
}
71+
72+
resource "aws_ecs_task_definition" "invite_bot" {
73+
count = local.invite_bot_count
74+
family = "${var.name_prefix}-invite-bot"
75+
requires_compatibilities = ["FARGATE"]
76+
network_mode = "awsvpc"
77+
cpu = tostring(var.invite_bot_cpu)
78+
memory = tostring(var.invite_bot_memory)
79+
execution_role_arn = var.ecs_task_execution_role_arn
80+
task_role_arn = var.ecs_task_role_arn
81+
82+
container_definitions = jsonencode([
83+
{
84+
name = local.invite_bot_container_name
85+
image = var.invite_bot_image
86+
essential = true
87+
portMappings = [{
88+
containerPort = 8083
89+
hostPort = 8083
90+
protocol = "tcp"
91+
}]
92+
environment = [
93+
for k, v in merge({ HTTP_ADDR = ":8083" }, var.invite_bot_environment) : {
94+
name = k
95+
value = v
96+
}
97+
]
98+
logConfiguration = {
99+
logDriver = "awslogs"
100+
options = {
101+
awslogs-group = aws_cloudwatch_log_group.invite_bot[0].name
102+
awslogs-region = data.aws_region.current.region
103+
awslogs-stream-prefix = "invite-bot"
104+
}
105+
}
106+
}
107+
])
108+
109+
tags = var.tags
110+
}
111+
112+
resource "aws_ecs_service" "invite_bot" {
113+
count = local.invite_bot_count
114+
name = "invite-bot"
115+
cluster = aws_ecs_cluster.backend.id
116+
task_definition = aws_ecs_task_definition.invite_bot[0].arn
117+
desired_count = 1
118+
launch_type = "FARGATE"
119+
120+
deployment_minimum_healthy_percent = 0
121+
deployment_maximum_percent = 100
122+
123+
network_configuration {
124+
assign_public_ip = false
125+
security_groups = [aws_security_group.invite_bot[0].id]
126+
subnets = var.private_subnet_ids
127+
}
128+
129+
service_registries {
130+
registry_arn = aws_service_discovery_service.invite_bot[0].arn
131+
}
132+
133+
tags = var.tags
134+
}

modules/ecs/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ resource "aws_ecs_task_definition" "backend" {
149149
protocol = "tcp"
150150
}]
151151
environment = [
152-
for k, v in var.backend_environment : {
152+
for k, v in local.backend_environment_effective : {
153153
name = k
154154
value = v
155155
}

modules/ecs/outputs.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,11 @@ output "backend_ecs_cluster_name" {
1717
output "backend_ecs_service_name" {
1818
value = aws_ecs_service.backend.name
1919
}
20+
21+
output "invite_bot_service_name" {
22+
value = var.invite_bot_enabled ? aws_ecs_service.invite_bot[0].name : null
23+
}
24+
25+
output "invite_bot_internal_url" {
26+
value = var.invite_bot_enabled ? "http://${aws_service_discovery_service.invite_bot[0].name}.${aws_service_discovery_private_dns_namespace.internal[0].name}:8083" : null
27+
}

modules/ecs/variables.tf

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,30 @@ variable "backend_health_check_timeout_seconds" { type = number }
2525
variable "backend_health_check_healthy_threshold" { type = number }
2626
variable "backend_health_check_unhealthy_threshold" { type = number }
2727

28+
variable "invite_bot_enabled" {
29+
type = bool
30+
default = false
31+
}
32+
variable "invite_bot_image" {
33+
type = string
34+
default = ""
35+
}
36+
variable "invite_bot_cpu" {
37+
type = number
38+
default = 256
39+
}
40+
variable "invite_bot_memory" {
41+
type = number
42+
default = 512
43+
}
44+
variable "invite_bot_environment" {
45+
type = map(string)
46+
default = {}
47+
}
48+
variable "invite_bot_log_retention_days" {
49+
type = number
50+
default = 14
51+
}
52+
2853
variable "ecs_task_execution_role_arn" { type = string }
2954
variable "ecs_task_role_arn" { type = string }

variables.tf

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,36 @@ variable "backend_environment" {
139139
default = {}
140140
}
141141

142+
variable "invite_bot_enabled" {
143+
type = bool
144+
default = false
145+
}
146+
147+
variable "invite_bot_image" {
148+
type = string
149+
default = ""
150+
}
151+
152+
variable "invite_bot_cpu" {
153+
type = number
154+
default = 256
155+
}
156+
157+
variable "invite_bot_memory" {
158+
type = number
159+
default = 512
160+
}
161+
162+
variable "invite_bot_environment" {
163+
type = map(string)
164+
default = {}
165+
}
166+
167+
variable "invite_bot_log_retention_days" {
168+
type = number
169+
default = 14
170+
}
171+
142172
variable "backend_log_retention_days" {
143173
type = number
144174
description = "CloudWatch log retention days for backend ECS logs."

0 commit comments

Comments
 (0)