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
8 changes: 8 additions & 0 deletions docker/nginx/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ server {
return 200;
}

# Route ALB health checks to the API container
location = /health {
proxy_pass http://api:8000/health;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# Route to api
location /api/ {
proxy_pass http://api:8000/api/;
Expand Down
64 changes: 42 additions & 22 deletions terraform/modules/app_service/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ resource "aws_security_group" "instance" {
vpc_id = data.aws_vpc.main.id

ingress {
from_port = var.container_port
to_port = var.container_port
from_port = 80
to_port = 80
protocol = "tcp"
security_groups = var.is_test_env ? null : [aws_security_group.alb[0].id]
cidr_blocks = var.is_test_env ? concat([data.aws_vpc.main.cidr_block], var.additional_vpc_cidrs) : null
Expand Down Expand Up @@ -107,10 +107,28 @@ resource "aws_iam_role" "instance_role" {
tags = local.common_tags
}

# Found the Lake Formation tags should be created outside of this module to avoid conflicts
#resource "aws_lakeformation_lf_tag" "team_tag" {
# key = "Team"
# values = ["EDFS"]
# lifecycle {
# prevent_destroy = true
# }
#}

#resource "aws_lakeformation_lf_tag" "env_tag" {
# key = "Environment"
# # Currently limited to oe and test
# values = ["oe", "test"]
# lifecycle {
# prevent_destroy = true
# }
#}

resource "aws_lakeformation_permissions" "icefabric_tbl" {
principal = aws_iam_role.instance_role.arn
permissions = ["DESCRIBE", "SELECT"]
catalog_id = data.aws_caller_identity.current.account_id
catalog_id = data.aws_caller_identity.current.account_id

lf_tag_policy {
resource_type = "TABLE"
Expand All @@ -122,7 +140,7 @@ resource "aws_lakeformation_permissions" "icefabric_tbl" {

expression {
key = "Environment"
values = ["Test"]
values = [var.environment]
}
}
}
Expand Down Expand Up @@ -153,8 +171,8 @@ resource "aws_iam_role_policy" "instance_policy" {
"logs:PutLogEvents"
]
Resource = [
"${aws_cloudwatch_log_group.api_logs.arn}:*",
aws_cloudwatch_log_group.api_logs.arn
"${aws_cloudwatch_log_group.app_logs.arn}:*",
aws_cloudwatch_log_group.app_logs.arn
]
},
{
Expand Down Expand Up @@ -225,7 +243,7 @@ resource "aws_instance" "test_instance" {
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
http_put_response_hop_limit = 2
}

iam_instance_profile = aws_iam_instance_profile.instance_profile.name
Expand All @@ -236,17 +254,18 @@ resource "aws_instance" "test_instance" {
user_data_replace_on_change = true
user_data_base64 = base64encode(templatefile("${path.module}/templates/user_data.sh.tpl", {
aws_region = var.aws_region
container_port = var.container_port
s3_bucket = trimsuffix(var.data_lake_bucket_arn, "/*")
directory_id = var.directory_id,
directory_name = var.directory_name,
ad_secret = var.ad_secret,
ad_dns_1 = var.ad_dns_1,
ad_dns_2 = var.ad_dns_2,
log_group_name = aws_cloudwatch_log_group.api_logs.name
log_group_name = aws_cloudwatch_log_group.app_logs.name
environment = var.environment
docker_image_uri = var.docker_image_uri
deployment_timestamp = var.deployment_timestamp
nginx_image_uri = var.nginx_image_uri
api_image_uri = var.api_image_uri
dashboard_image_uri = var.dashboard_image_uri
nginx_conf = file(var.nginx_conf_path)
}))

tags = merge(local.common_tags, {
Expand Down Expand Up @@ -274,7 +293,7 @@ resource "aws_launch_template" "app" {
}

block_device_mappings {
device_name = "/dev/xvda"
device_name = "/dev/sda1"
ebs {
volume_size = var.root_volume_size
volume_type = var.root_volume_type
Expand All @@ -287,7 +306,7 @@ resource "aws_launch_template" "app" {
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
http_put_response_hop_limit = 2
}

iam_instance_profile {
Expand All @@ -296,17 +315,18 @@ resource "aws_launch_template" "app" {

user_data = base64encode(templatefile("${path.module}/templates/user_data.sh.tpl", {
aws_region = var.aws_region
container_port = var.container_port
s3_bucket = trimsuffix(var.data_lake_bucket_arn, "/*")
directory_id = var.directory_id,
directory_name = var.directory_name,
ad_secret = var.ad_secret,
ad_dns_1 = "10.3.1.74",
ad_dns_2 = "10.3.0.60",
log_group_name = aws_cloudwatch_log_group.api_logs.name
ad_dns_1 = var.ad_dns_1,
ad_dns_2 = var.ad_dns_2,
log_group_name = aws_cloudwatch_log_group.app_logs.name
environment = var.environment
docker_image_uri = var.docker_image_uri
deployment_timestamp = var.deployment_timestamp
nginx_image_uri = var.nginx_image_uri
api_image_uri = var.api_image_uri
dashboard_image_uri = var.dashboard_image_uri
nginx_conf = file(var.nginx_conf_path)
}))

monitoring {
Expand Down Expand Up @@ -404,7 +424,7 @@ resource "aws_lb_target_group" "app" {
count = var.is_test_env ? 0 : 1

name = "${var.app_name}-${var.environment}"
port = var.container_port
port = 80
protocol = "HTTP"
vpc_id = data.aws_vpc.main.id

Expand All @@ -413,7 +433,7 @@ resource "aws_lb_target_group" "app" {
healthy_threshold = 3
interval = 30
matcher = "200" # Accept 200 from the version endpoint
path = "/version/"
path = "/health"
port = "traffic-port"
timeout = 10
unhealthy_threshold = 3
Expand Down Expand Up @@ -608,7 +628,7 @@ resource "aws_route53_record" "app" {
}

# CloudWatch Resources
resource "aws_cloudwatch_log_group" "api_logs" {
resource "aws_cloudwatch_log_group" "app_logs" {
name = "/aws/ec2/${var.app_name}-${var.environment}"
retention_in_days = var.log_retention_days

Expand Down
2 changes: 1 addition & 1 deletion terraform/modules/app_service/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ output "instance_role_arn" {

output "cloudwatch_log_group_name" {
description = "Name of the CloudWatch log group"
value = aws_cloudwatch_log_group.api_logs.name
value = aws_cloudwatch_log_group.app_logs.name
}

output "alb_dns_name" {
Expand Down
88 changes: 56 additions & 32 deletions terraform/modules/app_service/templates/user_data.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ apt-get install -y \
collectd systemd-timesyncd net-tools \
realmd sssd sssd-tools libnss-sss libpam-sss \
adcli samba-common-bin oddjob oddjob-mkhomedir \
packagekit krb5-user
packagekit krb5-user docker-compose-v2

# === Configure Kerberos ===
cat > /etc/krb5.conf <<EOF
Expand All @@ -112,29 +112,15 @@ cat > /etc/krb5.conf <<EOF
$DOMAIN_NAME = $REALM_NAME
EOF

# === Install Docker ===
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
wait_for_apt_lock
apt-get update -y
wait_for_apt_lock
apt-get install -y docker-ce docker-ce-cli containerd.io

# === Docker Compose v2 ===
curl -sSL "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# === Start Docker ===
systemctl enable docker
systemctl start docker

# === CloudWatch Agent ===
wget -q https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
dpkg -i -E amazon-cloudwatch-agent.deb

cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json <<EOF
{
"agent": {
"run_as_user": "root"
},
"metrics": {
"namespace": "System/Linux",
"metrics_collected": {
Expand All @@ -145,6 +131,20 @@ cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json <<EOF
"metrics_collection_interval": 60
}
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/opt/icefabric/logs/icefabric.log",
"log_group_name": "${log_group_name}",
"log_stream_name": "{instance_id}",
"timezone": "UTC"
}
]
}
}
}
}
EOF
Expand Down Expand Up @@ -238,16 +238,21 @@ getent passwd "$AD_USER@$DOMAIN_NAME" || echo "SSSD user lookup failed"

echo "Domain join and configuration complete"


# === Setup Application Directory ===
mkdir -p /opt/icefabric /opt/icefabric/logs
mkdir -p /opt/icefabric /opt/icefabric/logs /opt/icefabric/nginx

# === Write Nginx Configuration ===
# Using 'EOF' with single quotes prevents bash from evaluating variables inside the block,
# ensuring the raw Nginx config from Terraform is written exactly as-is.
cat > /opt/icefabric/nginx/default.conf <<'EOF'
${nginx_conf}
EOF

# === .env File for Compose ===
cat > /opt/icefabric/.env <<EOF

APP_HOST=0.0.0.0
APP_PORT=8000
RELOAD=true
RELOAD=false
CATALOG_PATH=glue
LOG_DIR=/opt/icefabric/logs/
AWS_DEFAULT_REGION=${aws_region}
Expand All @@ -258,25 +263,44 @@ EOF
# === Docker Compose Application Stack ===
cat > /opt/icefabric/docker-compose.yml <<EOF
services:
myapp:
image: ${docker_image_uri}
network_mode: "host"
nginx:
image: ${nginx_image_uri}
container_name: icefabric-nginx-proxy
ports:
- "8000:8000"
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api
- dashboard
restart: always

api:
image: ${api_image_uri}
container_name: icefabric-api
ports:
- "127.0.0.1:8000:8000"
env_file:
- ./.env
restart: always
volumes:
- /opt/icefabric/logs:/app/logs
command: /bin/bash -c "uvicorn app.main:app --host 0.0.0.0 --port 8000 >> /app/logs/app.log 2>&1"
healthcheck:
test: ["CMD", "curl", "-f", "--head", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 900s
start_period: 60s

dashboard:
image: ${dashboard_image_uri}
container_name: icefabric-dashboard
ports:
- "127.0.0.1:8501:8501"
env_file:
- ./.env
restart: always
EOF


# === Compose Systemd Service ===
cat > /etc/systemd/system/icefabric.service <<EOF
[Unit]
Expand All @@ -287,8 +311,8 @@ After=docker.service
[Service]
Restart=always
WorkingDirectory=/opt/icefabric
ExecStart=/usr/local/bin/docker-compose up
ExecStop=/usr/local/bin/docker-compose down
ExecStart=/bin/sh -c '/usr/bin/docker compose up >> /opt/icefabric/logs/icefabric.log 2>&1'
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
Expand Down
21 changes: 19 additions & 2 deletions terraform/modules/app_service/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,26 @@ variable "app_name" {
type = string
}

variable "docker_image_uri" {
description = "The full URI of the Docker image to deploy (e.g., ghcr.io/ngwpc/icefabric:latest)"
variable "nginx_image_uri" {
description = "Docker image URI for the Nginx proxy"
type = string
default = "nginx:latest"
}

variable "api_image_uri" {
description = "Docker image URI for the API (e.g., ghcr.io/ngwpc/icefabric:latest)"
type = string
}

variable "dashboard_image_uri" {
description = "Docker image URI for the Dashboard"
type = string
}

variable "nginx_conf_path" {
description = "Local file path to the Nginx default.conf"
type = string
default = "../../../docker/nginx/default.conf"
}

variable "vpc_name" {
Expand Down
Loading