From ce3ea16e002c708bf37b34ea1f11052e052002ff Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:47:45 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20config.alloy=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/side-infra/config.alloy | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 config/side-infra/config.alloy diff --git a/config/side-infra/config.alloy b/config/side-infra/config.alloy new file mode 100644 index 0000000..bd3aedf --- /dev/null +++ b/config/side-infra/config.alloy @@ -0,0 +1,37 @@ +livedebugging { + enabled = true +} + +logging { + level = "info" + format = "logfmt" +} + +local.file_match "spring_logs" { + path_targets = [{ __path__ = "/var/log/spring/*.log" }] // 서비스 로그 파일 경로 +} + +loki.source.file "spring_source" { + targets = local.file_match.spring_logs.targets // 위에서 정의한 로그 파일 경로 사용 + forward_to = [loki.process.spring_labels.receiver] // 읽은 로그를 처리 단계로 전달 +} + +loki.process "spring_labels" { + forward_to = [loki.write.grafana_loki.receiver] // 처리된 로그를 Loki로 전송 + + stage.static_labels { + values = { + service = "backend", + env = sys.env("ALLOY_ENV"), + } + } +} + +loki.write "grafana_loki" { + endpoint { + url = "http://monitor.solid-connection.com:3100/loki/api/v1/push" + tenant_id = "fake" // Loki 테넌트 ID (싱글 테넌시이기에 fake로 설정) + batch_wait = "1s" // 로그 배치 전송 대기 시간 + batch_size = "1MB" // 로그 배치 크기 + } +} From 300f560785094488ccc292a8b19bde99603d2cbb Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:48:20 +0900 Subject: [PATCH 2/6] =?UTF-8?q?fix:=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=AC=B4=ED=95=9C?= =?UTF-8?q?=EC=A0=95=20=EB=8C=80=EA=B8=B0=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/app_stack/scripts/docker_setup.sh | 11 +------ .../app_stack/scripts/nginx_setup.sh.tftpl | 31 +++++++------------ 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/modules/app_stack/scripts/docker_setup.sh b/modules/app_stack/scripts/docker_setup.sh index 1254eaa..a36799e 100644 --- a/modules/app_stack/scripts/docker_setup.sh +++ b/modules/app_stack/scripts/docker_setup.sh @@ -1,13 +1,4 @@ -wait_for_apt_lock() { - echo "Checking for apt locks..." - while fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \ - fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \ - fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do - echo "Waiting for apt lock..." - sleep 5 - done - echo "Apt lock acquired." -} +#!/bin/bash # 1. 필수 패키지 설치 apt-get update diff --git a/modules/app_stack/scripts/nginx_setup.sh.tftpl b/modules/app_stack/scripts/nginx_setup.sh.tftpl index bce1b2e..4e28002 100644 --- a/modules/app_stack/scripts/nginx_setup.sh.tftpl +++ b/modules/app_stack/scripts/nginx_setup.sh.tftpl @@ -1,34 +1,25 @@ -set -e +#!/bin/bash -wait_for_apt_lock() { - echo "Checking for apt locks..." - while fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \ - fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \ - fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do - echo "Waiting for apt lock..." - sleep 5 - done - echo "Apt lock acquired." -} +set -e -# --- 변수 설정 --- +# --- variables setting --- DOMAIN="${domain_name}" EMAIL="${email}" CONF_NAME="${conf_file_name}" echo "Start Nginx Setup for $DOMAIN with config file: $CONF_NAME" -# 1. Nginx 및 Certbot 필수 패키지 설치 +# 1. Install necessary packages for Nginx and Certbot apt-get update apt-get install -y nginx python3 python3-venv libaugeas0 -# 2. Certbot 설치 (pip 이용) +# 2. Install Certbot (using pip) python3 -m venv /opt/certbot/ /opt/certbot/bin/pip install --upgrade pip /opt/certbot/bin/pip install certbot certbot-nginx -ln -s /opt/certbot/bin/certbot /usr/bin/certbot +ln -sf /opt/certbot/bin/certbot /usr/bin/certbot -# 3. SSL 인증서 발급 (비대화형 모드) +# 3. Issue SSL certificate (Non-interactive mode) systemctl stop nginx certbot certonly --standalone \ @@ -39,7 +30,7 @@ certbot certonly --standalone \ echo "Certificate obtained successfully." -# 4. Nginx 설정 파일 작성 +# 4. Create Nginx configuration file cat < /etc/nginx/sites-available/$CONF_NAME server { listen 80; @@ -76,14 +67,14 @@ server { } EOF -# 5. 심볼릭 링크 연결 및 기본 설정 삭제 +# 5. Create symbolic link and remove default configuration ln -sf /etc/nginx/sites-available/$CONF_NAME /etc/nginx/sites-enabled/$CONF_NAME rm -f /etc/nginx/sites-enabled/default -# 6. 자동 갱신 크론탭 등록 +# 6. Register auto-renewal cron job echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew -q" | tee -a /etc/crontab > /dev/null -# 7. Nginx 재시작 +# 7. Nginx restart nginx -t systemctl restart nginx From cff64340168aea6a95b16b50929161b3df6f5069 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:48:40 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20side=20infra=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=20=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scripts/side_infra_setup.sh.tftpl | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 modules/app_stack/scripts/side_infra_setup.sh.tftpl diff --git a/modules/app_stack/scripts/side_infra_setup.sh.tftpl b/modules/app_stack/scripts/side_infra_setup.sh.tftpl new file mode 100644 index 0000000..6e87f45 --- /dev/null +++ b/modules/app_stack/scripts/side_infra_setup.sh.tftpl @@ -0,0 +1,77 @@ +#!/bin/bash + +set -e + +# --- Variable Configuration --- +WORK_DIR='${work_dir}' +ALLOY_ENV_NAME='${alloy_env_name}' # production or dev +ALLOY_CONFIG_CONTENT='${alloy_config_content}' + +REDIS_VERSION='${redis_version}' +REDIS_EXPORTER_VERSION='${redis_exporter_version}' +ALLOY_VERSION='${alloy_version}' + +echo "Start Side Infrastructure Setup..." + +# 1. Create working and log directories +mkdir -p "$WORK_DIR/config/side-infra" +mkdir -p "$WORK_DIR/logs" +# Set log directory permissions (Shared by App and Alloy) +chmod 777 "$WORK_DIR/logs" +chown -R ubuntu:ubuntu "$WORK_DIR" + +# 2. Create Alloy configuration file +cat < "$WORK_DIR/config/side-infra/config.alloy" +$ALLOY_CONFIG_CONTENT +EOF + +# 3. Create docker-compose.yml for side infrastructure +cat < "$WORK_DIR/docker-compose.side-infra.yml" +version: '3.8' + +services: + redis: + image: redis:$REDIS_VERSION + container_name: redis + network_mode: "host" + restart: always + command: redis-server --bind 127.0.0.1 --protected-mode yes + + redis-exporter: + image: oliver006/redis_exporter:$REDIS_EXPORTER_VERSION + container_name: redis-exporter + environment: + REDIS_ADDR: "127.0.0.1:6379" + depends_on: + - redis + network_mode: "host" + restart: always + + alloy: + image: grafana/alloy:$ALLOY_VERSION + container_name: alloy + volumes: + - ./logs:/var/log/spring:ro + - ./config/side-infra/config.alloy:/etc/alloy/config.alloy:ro + environment: + - ALLOY_ENV=$ALLOY_ENV_NAME + network_mode: "host" + restart: always + +EOF + +# 4. Start containers +cd "$WORK_DIR" + +# Cleanup potential conflicts where ports might be locked by other Docker Compose instances. +# 'rm -f' is executed first to ensure cleanup of any lingering network resources attached to existing containers. +echo "Cleanup: Force removing leftover containers..." +docker rm -f redis redis-exporter alloy || true + +echo "Stopping previous containers if running..." +docker compose -f docker-compose.side-infra.yml down || true + +echo "Starting containers..." +docker compose -f docker-compose.side-infra.yml up -d + +echo "Side Infrastructure Setup Complete!" \ No newline at end of file From a863f0eb8ad9be01148cc46214d238f488f7d5fd Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:49:19 +0900 Subject: [PATCH 4/6] =?UTF-8?q?docs:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=97=90=20=EB=94=B0=EB=A5=B8=20README.md=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/secrets | 2 +- modules/app_stack/ec2.tf | 100 ++++++++++++++++++++++++++++----- modules/app_stack/variables.tf | 53 ++++++++++++++--- 3 files changed, 133 insertions(+), 22 deletions(-) diff --git a/config/secrets b/config/secrets index c1cf69a..83f1768 160000 --- a/config/secrets +++ b/config/secrets @@ -1 +1 @@ -Subproject commit c1cf69a9de6f6b766750395875cd5bdcb16a0e96 +Subproject commit 83f176821a253bb18f5ca36f1f24e8ce2e7c91d7 diff --git a/modules/app_stack/ec2.tf b/modules/app_stack/ec2.tf index 704e676..b18f1fc 100644 --- a/modules/app_stack/ec2.tf +++ b/modules/app_stack/ec2.tf @@ -1,4 +1,4 @@ -# 3. CloudInit을 이용한 User Data 스크립트 구성 +# CloudInit을 이용한 User Data 스크립트 구성 data "cloudinit_config" "app_init" { gzip = true base64_encode = true @@ -9,20 +9,9 @@ data "cloudinit_config" "app_init" { content = file("${path.module}/scripts/docker_setup.sh") filename = "1_docker_install.sh" } - - # [Part 2] Nginx 설정 스크립트 - part { - content_type = "text/x-shellscript" - content = templatefile("${path.module}/scripts/nginx_setup.sh.tftpl", { - domain_name = var.domain_name - email = var.cert_email - conf_file_name = var.nginx_conf_name - }) - filename = "2_nginx_setup.sh" - } } -# 4. API Server (EC2) +# API Server (EC2) resource "aws_instance" "api_server" { ami = var.ami_id instance_type = var.instance_type @@ -51,3 +40,88 @@ resource "aws_instance" "api_server" { ] } } + +# 설정 및 컨테이너 실행 +# [리소스 1] Nginx 설정 변경 감지 및 실행 +resource "null_resource" "update_nginx" { + depends_on = [aws_instance.api_server] + + triggers = { + script_hash = sha256(templatefile("${path.module}/scripts/nginx_setup.sh.tftpl", { + domain_name = var.domain_name + email = var.cert_email + conf_file_name = var.nginx_conf_name + })) + } + + connection { + type = "ssh" + user = "ubuntu" + host = aws_instance.api_server.public_ip + private_key = file(var.ssh_key_path) + } + + provisioner "file" { + content = templatefile("${path.module}/scripts/nginx_setup.sh.tftpl", { + domain_name = var.domain_name + email = var.cert_email + conf_file_name = var.nginx_conf_name + }) + destination = "/tmp/update_nginx.sh" + } + + provisioner "remote-exec" { + inline = [ + "cloud-init status --wait > /dev/null", # Docker 설치 대기 + "chmod +x /tmp/update_nginx.sh", + "echo 'Running Updated Nginx Script...'", + "sudo /tmp/update_nginx.sh", + "rm /tmp/update_nginx.sh" + ] + } +} + +# [리소스 2] Side Infra 설정 변경 감지 및 실행 +resource "null_resource" "update_side_infra" { + depends_on = [aws_instance.api_server] + + triggers = { + script_hash = sha256(templatefile("${path.module}/scripts/side_infra_setup.sh.tftpl", { + work_dir = var.work_dir + alloy_env_name = var.alloy_env_name + alloy_config_content = var.alloy_config_content + redis_version = var.redis_version + redis_exporter_version = var.redis_exporter_version + alloy_version = var.alloy_version + })) + } + + connection { + type = "ssh" + user = "ubuntu" + host = aws_instance.api_server.public_ip + private_key = file(var.ssh_key_path) + } + + provisioner "file" { + content = templatefile("${path.module}/scripts/side_infra_setup.sh.tftpl", { + work_dir = var.work_dir + alloy_env_name = var.alloy_env_name + alloy_config_content = var.alloy_config_content + redis_version = var.redis_version + redis_exporter_version = var.redis_exporter_version + alloy_version = var.alloy_version + }) + destination = "/tmp/update_side_infra.sh" + } + + provisioner "remote-exec" { + inline = [ + "cloud-init status --wait > /dev/null", # Docker 설치 대기 + "chmod +x /tmp/update_side_infra.sh", + "echo 'Running Updated Side Infra Script...'", + "sudo /tmp/update_side_infra.sh", + "rm /tmp/update_side_infra.sh" + ] + } +} diff --git a/modules/app_stack/variables.tf b/modules/app_stack/variables.tf index 878b0fe..7d7ff8e 100644 --- a/modules/app_stack/variables.tf +++ b/modules/app_stack/variables.tf @@ -1,13 +1,13 @@ -variable "env_name" { - description = "환경 이름" +variable "env_name" { + description = "환경 이름 (prod/stage)" } -variable "instance_type" { - description = "EC2 인스턴스 타입" +variable "instance_type" { + description = "EC2 인스턴스 타입" } -variable "db_instance_class" { - description = "RDS 인스턴스 타입" +variable "db_instance_class" { + description = "RDS 인스턴스 타입" } variable "api_ingress_rules" { @@ -74,8 +74,8 @@ variable "kms_key_arn" { type = string } -variable "vpc_id" { - description = "배포할 VPC ID" +variable "vpc_id" { + description = "배포할 VPC ID" } variable "ami_id" { @@ -114,3 +114,40 @@ variable "s3_upload_bucket_name" { description = "Name of the upload S3 bucket" type = string } + +# [Remote SSH용 변수] +variable "ssh_key_path" { + description = "Path to the SSH private key file for remote-exec" + type = string +} + +# [Side Infrastructure 관련 변수] +variable "work_dir" { + description = "Working directory for the application" + type = string +} + +variable "alloy_env_name" { + description = "Alloy Env Name" + type = string +} + +variable "alloy_config_content" { + description = "Content of the alloy configuration file (Read from file)" + type = string +} + +variable "redis_version" { + description = "Docker image tag for Redis" + type = string +} + +variable "redis_exporter_version" { + description = "Docker image tag for Redis Exporter" + type = string +} + +variable "alloy_version" { + description = "Docker image tag for Grafana Alloy" + type = string +} From db4c8bbb6b3a6162a39f2a43e4611df576ecb5ca Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:49:42 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EC=B6=94=EA=B0=80=EB=90=9C=20side?= =?UTF-8?q?=20infra=20=EB=B0=B0=ED=8F=AC=20=EA=B4=80=EB=A0=A8=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=EC=9D=84=20prod/stage=20=ED=99=98=EA=B2=BD=EC=97=90?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- environment/prod/main.tf | 16 ++++++++++++++-- environment/prod/variables.tf | 30 ++++++++++++++++++++++++++++++ environment/stage/main.tf | 18 +++++++++++++++--- environment/stage/variables.tf | 30 ++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/environment/prod/main.tf b/environment/prod/main.tf index 49adda0..955e4ba 100644 --- a/environment/prod/main.tf +++ b/environment/prod/main.tf @@ -13,7 +13,7 @@ module "prod_stack" { # 키페어 및 접속 허용 key_name = var.key_name - + # 인스턴스 스펙 instance_type = var.server_instance_type db_instance_class = var.db_instance_class @@ -24,7 +24,7 @@ module "prod_stack" { # RDS 식별자 설정 rds_identifier = var.rds_identifier - + # DB 계정 정보 db_username = var.db_root_username db_password = var.db_root_password @@ -45,4 +45,16 @@ module "prod_stack" { # S3 버킷 이름 전달 s3_default_bucket_name = var.s3_default_bucket_name s3_upload_bucket_name = var.s3_upload_bucket_name + + # ssh key 경로 전달 + ssh_key_path = var.ssh_key_path + + # Side Infra 관련 변수 전달 + work_dir = var.work_dir + alloy_env_name = var.alloy_env_name + alloy_config_content = file("${path.root}/../../config/side-infra/config.alloy") + + redis_version = var.redis_version + redis_exporter_version = var.redis_exporter_version + alloy_version = var.alloy_version } diff --git a/environment/prod/variables.tf b/environment/prod/variables.tf index dfcf981..7100dca 100644 --- a/environment/prod/variables.tf +++ b/environment/prod/variables.tf @@ -103,3 +103,33 @@ variable "s3_upload_bucket_name" { description = "Name of the upload S3 bucket" type = string } + +variable "ssh_key_path" { + description = "Path to the SSH private key file for remote-exec" + type = string +} + +variable "work_dir" { + description = "Working directory for the application" + type = string +} + +variable "alloy_env_name" { + description = "Alloy Env Name" + type = string +} + +variable "redis_version" { + description = "Docker image tag for Redis" + type = string +} + +variable "redis_exporter_version" { + description = "Docker image tag for Redis Exporter" + type = string +} + +variable "alloy_version" { + description = "Docker image tag for Grafana Alloy" + type = string +} diff --git a/environment/stage/main.tf b/environment/stage/main.tf index 5fd2308..0b5eefa 100644 --- a/environment/stage/main.tf +++ b/environment/stage/main.tf @@ -13,9 +13,9 @@ module "stage_stack" { # 키페어 및 접속 허용 key_name = var.key_name - + # 인스턴스 스펙 - instance_type = var.server_instance_type + instance_type = var.server_instance_type db_instance_class = var.db_instance_class # 보안 그룹 규칙 @@ -24,7 +24,7 @@ module "stage_stack" { # RDS 식별자 설정 rds_identifier = var.rds_identifier - + # DB 계정 정보 db_username = var.db_root_username db_password = var.db_root_password @@ -45,4 +45,16 @@ module "stage_stack" { # S3 버킷 이름 전달 s3_default_bucket_name = var.s3_default_bucket_name s3_upload_bucket_name = var.s3_upload_bucket_name + + # ssh key 경로 전달 + ssh_key_path = var.ssh_key_path + + # Side Infra 관련 변수 전달 + work_dir = var.work_dir + alloy_env_name = var.alloy_env_name + alloy_config_content = file("${path.root}/../../config/side-infra/config.alloy") + + redis_version = var.redis_version + redis_exporter_version = var.redis_exporter_version + alloy_version = var.alloy_version } diff --git a/environment/stage/variables.tf b/environment/stage/variables.tf index 2e18f2a..0c31600 100644 --- a/environment/stage/variables.tf +++ b/environment/stage/variables.tf @@ -103,3 +103,33 @@ variable "s3_upload_bucket_name" { description = "Name of the upload S3 bucket" type = string } + +variable "ssh_key_path" { + description = "Path to the SSH private key file for remote-exec" + type = string +} + +variable "work_dir" { + description = "Working directory for the application" + type = string +} + +variable "alloy_env_name" { + description = "Alloy Env Name" + type = string +} + +variable "redis_version" { + description = "Docker image tag for Redis" + type = string +} + +variable "redis_exporter_version" { + description = "Docker image tag for Redis Exporter" + type = string +} + +variable "alloy_version" { + description = "Docker image tag for Grafana Alloy" + type = string +} From b00343556fc405d039055399550602c255d0e960 Mon Sep 17 00:00:00 2001 From: Hexeong <123macanic@naver.com> Date: Sat, 27 Dec 2025 20:52:43 +0900 Subject: [PATCH 6/6] =?UTF-8?q?docs:=20=EC=B6=94=EA=B0=80=EB=90=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=97=90=20=EB=94=B0=EB=A5=B8=20README.md=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d5ae6d..fc3e2ce 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,16 @@ solid-connection 서비스의 AWS, terraform 기반 IaC 레포지토리입니다 ``` solid-connection-infra/ ├── config/ -│ └── secrets/ # 민감한 data 관리 -│ └── ... +│ ├── secrets/ # 민감한 data 관리 +│ │ └── ... +│ └── side-infra/ # [side infra 관련 설정] +│ └── config.alloy ├── modules/ │ └── app_stack/ # [Prod/Stage 환경의 공통 모듈] +│ ├── scripts +│ │ └── docker_setup.sh +│ │ └── nginx_setup.sh.tftpl +│ │ └── side_infra_setup.sh.tftpl │ ├── security_groups.tf │ ├── ec2.tf │ ├── rds.tf