From 2bb87b4474f137224d16e571cb394e2573bc6299 Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 01:19:27 +0900 Subject: [PATCH 01/35] =?UTF-8?q?fix(#321):=20k8s=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=20=EB=B3=80=EC=88=98=20=EB=AC=B8=EC=A0=9C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EC=97=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connectors/register-connectors.sh | 23 +++++++- infra/k8s/base/kafka.yaml | 97 ------------------------------- 2 files changed, 20 insertions(+), 100 deletions(-) delete mode 100644 infra/k8s/base/kafka.yaml diff --git a/connectors/register-connectors.sh b/connectors/register-connectors.sh index 8049533b..7c4a201f 100644 --- a/connectors/register-connectors.sh +++ b/connectors/register-connectors.sh @@ -1,10 +1,27 @@ #!/bin/sh + +TARGET_URL=${CONNECT_URL:-"http://connect:8083"} + echo "Waiting for Kafka Connect..." -while [ $(curl -s -o /dev/null -w "%{http_code}" http://connect:8083) -ne 200 ]; do +while [ $(curl -s -o /dev/null -w "%{http_code}" $TARGET_URL) -ne 200 ]; do sleep 3 done echo "Registering connectors from /configs..." for file in /configs/*.json; do - curl -X POST -H "Content-Type: application/json" -d @"$file" http://connect:8083/connectors -done \ No newline at end of file + filename=$(basename "$file") + echo "Processing $filename..." + + sed -e "s|\${env:DB_HOST}|$DB_HOST|g" \ + -e "s|\${env:SPRING_DATASOURCE_USERNAME}|$SPRING_DATASOURCE_USERNAME|g" \ + -e "s|\${env:SPRING_DATASOURCE_PASSWORD}|$SPRING_DATASOURCE_PASSWORD|g" \ + -e "s|\${env:DB_NAME}|$DB_NAME|g" \ + "$file" > "/tmp/$filename" + + response=$(curl -s -X POST -H "Content-Type: application/json" \ + -d @"/tmp/$filename" \ + $TARGET_URL/connectors) + echo "Response for $filename: $response" +done + +echo "ALL connectors Created" \ No newline at end of file diff --git a/infra/k8s/base/kafka.yaml b/infra/k8s/base/kafka.yaml deleted file mode 100644 index 8ad08157..00000000 --- a/infra/k8s/base/kafka.yaml +++ /dev/null @@ -1,97 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: kafka-pvc - namespace: spot -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: kafka - namespace: spot -spec: - replicas: 1 - selector: - matchLabels: - app: kafka - template: - metadata: - labels: - app: kafka - spec: - enableServiceLinks: false - containers: - - name: kafka - image: confluentinc/cp-kafka:7.5.0 - ports: - - containerPort: 9092 - - containerPort: 29092 - env: - - name: KAFKA_NODE_ID - value: "1" - - name: KAFKA_PROCESS_ROLES - value: "broker,controller" - - name: KAFKA_CONTROLLER_QUORUM_VOTERS - value: "1@localhost:19093" - - name: KAFKA_LISTENERS - value: "INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:19093" - - name: KAFKA_ADVERTISED_LISTENERS - value: "INTERNAL://kafka:29092,EXTERNAL://kafka:9092" - - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP - value: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT" - - name: KAFKA_INTER_BROKER_LISTENER_NAME - value: "INTERNAL" - - name: KAFKA_CONTROLLER_LISTENER_NAMES - value: "CONTROLLER" - - name: CLUSTER_ID - value: "J9Xz7kQPRYyK8VkqH3mW5A" - - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR - value: "1" - - name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR - value: "1" - - name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR - value: "1" - - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE - value: "true" - - name: KAFKA_NUM_PARTITIONS - value: "3" - - name: KAFKA_HEAP_OPTS - value: "-Xmx512M -Xms512M" - - name: KAFKA_LOG4J_ROOT_LOGLEVEL - value: "WARN" - volumeMounts: - - name: kafka-storage - mountPath: /var/lib/kafka/data - resources: - requests: - memory: "512Mi" - cpu: "250m" - limits: - memory: "1Gi" - cpu: "500m" - volumes: - - name: kafka-storage - persistentVolumeClaim: - claimName: kafka-pvc ---- -apiVersion: v1 -kind: Service -metadata: - name: kafka - namespace: spot -spec: - selector: - app: kafka - ports: - - name: external - port: 9092 - targetPort: 9092 - - name: internal - port: 29092 - targetPort: 29092 \ No newline at end of file From 8ee891f00a22b06b8ac0c5c21f6148cb7a6aadc8 Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 01:20:47 +0900 Subject: [PATCH 02/35] =?UTF-8?q?fix(#321):=20kafka-connect,=20kafka-conne?= =?UTF-8?q?ct-init,=20kafka-ui=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/k8s/base/kafka/kafka-connect-init.yaml | 35 +++++++ infra/k8s/base/kafka/kafka-connect.yaml | 61 ++++++++++++ infra/k8s/base/kafka/kafka-ui.yaml | 48 ++++++++++ infra/k8s/base/kafka/kafka.yaml | 98 ++++++++++++++++++++ infra/k8s/kustomization.yaml | 30 +++++- run_k3d.sh | 2 + 6 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 infra/k8s/base/kafka/kafka-connect-init.yaml create mode 100644 infra/k8s/base/kafka/kafka-connect.yaml create mode 100644 infra/k8s/base/kafka/kafka-ui.yaml create mode 100644 infra/k8s/base/kafka/kafka.yaml diff --git a/infra/k8s/base/kafka/kafka-connect-init.yaml b/infra/k8s/base/kafka/kafka-connect-init.yaml new file mode 100644 index 00000000..d4eb3977 --- /dev/null +++ b/infra/k8s/base/kafka/kafka-connect-init.yaml @@ -0,0 +1,35 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: kafka-connect-init + namespace: spot +spec: + ttlSecondsAfterFinished: 60 # 해당 job 성공 후 60초 뒤에 pod 자동 삭제 + template: + spec: + restartPolicy: OnFailure + containers: + - name: kafka-connect-init + image: curlimages/curl:latest + env: + - name: CONNECT_URL + value: "http://kafka-connect-svc:8083" + envFrom: + - secretRef: + name: spot-secrets + command: ["/bin/sh"] + args: ["/configs/register-connectors.sh"] + volumeMounts: + - name: config-volume + mountPath: /configs + resources: + requests: + memory: "32Mi" + cpu: "10m" + limits: + memory: "64Mi" + cpu: "50m" + volumes: + - name: config-volume + configMap: + name: kafka-connect-init-config \ No newline at end of file diff --git a/infra/k8s/base/kafka/kafka-connect.yaml b/infra/k8s/base/kafka/kafka-connect.yaml new file mode 100644 index 00000000..87684cc8 --- /dev/null +++ b/infra/k8s/base/kafka/kafka-connect.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka-connect + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka-connect + template: + metadata: + labels: + app: kafka-connect + spec: + containers: + - name: kafka-connect + image: quay.io/debezium/connect:3.4.0.Final + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8083 + env: + - name: CONNECT_CONFIG_PROVIDERS + value: 'env' + - name: CONNECT_CONFIG_PROVIDERS_ENV_CLASS + value: 'org.apache.kafka.common.config.provider.EnvVarConfigProvider' + - name: CONNECT_BOOTSTRAP_SERVERS + value: kafka:9092 + - name: GROUP_ID + value: "1" + - name: CONFIG_STORAGE_TOPIC + value: my_connect_configs + - name: OFFSET_STORAGE_TOPIC + value: my_connect_offsets + - name: STATUS_STORAGE_TOPIC + value: my_connect_statuses + - name: KEY_CONVERTER + value: org.apache.kafka.connect.json.JsonConverter + - name: VALUE_CONVERTER + value: org.apache.kafka.connect.json.JsonConverter + - name: CONNECT_KEY_CONVERTER_SCHEMAS_ENABLE + value: "false" + - name: CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE + value: "false" + - name: LOGGING_LEVEL + value: 'WARN' + - name: CONNECT_LOG4J_LOGGERS + value: "org.apache.kafka.connect.runtime.rest=WARN,org.reflections=ERROR" +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka-connect-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: kafka-connect + ports: + - port: 8083 + targetPort: 8083 \ No newline at end of file diff --git a/infra/k8s/base/kafka/kafka-ui.yaml b/infra/k8s/base/kafka/kafka-ui.yaml new file mode 100644 index 00000000..c5a4d6ee --- /dev/null +++ b/infra/k8s/base/kafka/kafka-ui.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka-ui + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka-ui + template: + metadata: + labels: + app: kafka-ui + spec: + containers: + - name: kafka-ui + image: provectuslabs/kafka-ui:latest + ports: + - containerPort: 8080 + env: + - name: SERVER_SERVLET_CONTEXT_PATH + value: '/kafka-ui' + - name: KAFKA_CLUSTERS_0_NAME + value: local-spot-cluster + - name: KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS + value: 'kafka:9092' + - name: KAFKA_CLUSTERS_0_KAFKACONNECT_0_NAME + value: connect + - name: KAFKA_CLUSTERS_0_KAFKACONNECT_0_ADDRESS + value: 'http://kafka-connect-svc:8083' + - name: LOGGING_LEVEL_ROOT + value: WARN + - name: LOGGING_LEVEL_COM_PROVECTUS + value: WARN +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka-ui-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: kafka-ui + ports: + - port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/infra/k8s/base/kafka/kafka.yaml b/infra/k8s/base/kafka/kafka.yaml new file mode 100644 index 00000000..800b81ae --- /dev/null +++ b/infra/k8s/base/kafka/kafka.yaml @@ -0,0 +1,98 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: kafka-pvc + namespace: spot +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka + template: + metadata: + labels: + app: kafka + spec: + enableServiceLinks: false + containers: + - name: kafka + image: apache/kafka:4.0.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9092 + - containerPort: 29092 + env: + - name: KAFKA_NODE_ID + value: "1" + - name: KAFKA_PROCESS_ROLES + value: "broker,controller" + - name: KAFKA_CONTROLLER_QUORUM_VOTERS + value: "1@localhost:19093" + - name: KAFKA_LISTENERS + value: "INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:19093" + - name: KAFKA_ADVERTISED_LISTENERS + value: "INTERNAL://kafka:29092,EXTERNAL://kafka:9092" + - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP + value: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT" + - name: KAFKA_INTER_BROKER_LISTENER_NAME + value: "INTERNAL" + - name: KAFKA_CONTROLLER_LISTENER_NAMES + value: "CONTROLLER" + - name: CLUSTER_ID + value: "J9Xz7kQPRYyK8VkqH3mW5A" + - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR + value: "1" + - name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR + value: "1" + - name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR + value: "1" + - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE + value: "true" + - name: KAFKA_NUM_PARTITIONS + value: "3" + - name: KAFKA_HEAP_OPTS + value: "-Xmx512M -Xms512M" + - name: KAFKA_LOG4J_ROOT_LOGLEVEL + value: "WARN" + volumeMounts: + - name: kafka-storage + mountPath: /var/lib/kafka/data + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + volumes: + - name: kafka-storage + persistentVolumeClaim: + claimName: kafka-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka + namespace: spot +spec: + selector: + app: kafka + ports: + - name: external + port: 9092 + targetPort: 9092 + - name: internal + port: 29092 + targetPort: 29092 \ No newline at end of file diff --git a/infra/k8s/kustomization.yaml b/infra/k8s/kustomization.yaml index 19a9af61..20282824 100644 --- a/infra/k8s/kustomization.yaml +++ b/infra/k8s/kustomization.yaml @@ -4,23 +4,37 @@ kind: Kustomization # namespace: spot 제거 - 각 리소스가 자체 네임스페이스 유지 resources: + # Base - base/namespace.yaml - base/configmap.yaml - base/postgres.yaml - base/redis.yaml - - base/kafka.yaml + + # Kafka + - base/kafka/kafka.yaml + - base/kafka/kafka-connect.yaml + - base/kafka/kafka-connect-init.yaml + - base/kafka/kafka-ui.yaml + + # Monitoring & Logging - base/monitoring/loki/loki.yaml - base/monitoring/loki/loki-config.yaml - base/monitoring/monitoring-ingress.yaml - - base/monitoring/grafana/grafana.yaml - - base/monitoring/grafana/grafana-config.yaml - base/monitoring/fluent-bit/fluent-bit.yaml - base/monitoring/fluent-bit/fluent-bit-config.yaml + + # Grafana + - base/monitoring/grafana/grafana.yaml + - base/monitoring/grafana/grafana-config.yaml + + # Spot Monitoring - base/monitoring/servicemonitors/spot-gateway-servicemonitor.yaml - base/monitoring/servicemonitors/spot-user-servicemonitor.yaml - base/monitoring/servicemonitors/spot-order-servicemonitor.yaml - base/monitoring/servicemonitors/spot-store-servicemonitor.yaml - base/monitoring/servicemonitors/spot-payment-servicemonitor.yaml + + # Spot Apps - apps/spot-ingress.yaml - apps/spot-gateway.yaml - apps/spot-user.yaml @@ -42,6 +56,16 @@ configMapGenerator: - ../../config/spot-payment.yml options: disableNameSuffixHash: true + + - name: kafka-connect-init-config + namespace: spot + files: + - ../../connectors/order-outbox.json + - ../../connectors/payment-outbox.json + - ../../connectors/register-connectors.sh + options: + disableNameSuffixHash: true + - name: grafana-dashboards-spot namespace: monitoring files: diff --git a/run_k3d.sh b/run_k3d.sh index 33424a67..20ca930d 100755 --- a/run_k3d.sh +++ b/run_k3d.sh @@ -162,6 +162,8 @@ deploy_all() { kubectl wait --for=condition=available deployment/postgres -n spot --timeout=180s kubectl wait --for=condition=available deployment/redis -n spot --timeout=180s kubectl wait --for=condition=available deployment/kafka -n spot --timeout=180s + kubectl wait --for=condition=available deployment/kafka-connect -n spot --timeout=180s + kubectl wait --for=condition=available deployment/kafka-ui -n spot --timeout=180s log_info "Infrastructure deployed successfully!" From bbba917f0e5a6092e52602913b2035755f13e763 Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 01:21:29 +0900 Subject: [PATCH 03/35] =?UTF-8?q?fix(#321):=20kafka-ui=20URL=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/k8s/apps/spot-ingress.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/infra/k8s/apps/spot-ingress.yaml b/infra/k8s/apps/spot-ingress.yaml index 303351d0..d9477dc4 100644 --- a/infra/k8s/apps/spot-ingress.yaml +++ b/infra/k8s/apps/spot-ingress.yaml @@ -19,3 +19,10 @@ spec: name: spot-gateway port: number: 80 + - path: /kafka-ui + pathType: Prefix + backend: + service: + name: kafka-ui-svc + port: + number: 80 \ No newline at end of file From 24963544ce165be8803a38f5ed2be2e0e83bf8ed Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 01:22:01 +0900 Subject: [PATCH 04/35] =?UTF-8?q?fix(#321):=20.env=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index a282fee4..8cacdc12 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -93,6 +93,8 @@ services: connect-init: image: curlimages/curl:latest container_name: connect-init + env_file: + - .env depends_on: - connect networks: @@ -118,6 +120,7 @@ services: networks: - spot-network +# --- Temporal 1--- temporal: container_name: temporal image: temporalio/auto-setup:1.29.3 From 59687aff19ac8ca74e9280d3e9b83f153b797453 Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 16:21:52 +0900 Subject: [PATCH 05/35] =?UTF-8?q?feat(#321):=20temporal=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/k8s/base/temporal/temporal-ui.yaml | 38 ++++++++++++++ infra/k8s/base/temporal/temporal.yaml | 67 ++++++++++++++++++++++++ infra/k8s/kustomization.yaml | 4 ++ run_k3d.sh | 2 + 4 files changed, 111 insertions(+) create mode 100644 infra/k8s/base/temporal/temporal-ui.yaml create mode 100644 infra/k8s/base/temporal/temporal.yaml diff --git a/infra/k8s/base/temporal/temporal-ui.yaml b/infra/k8s/base/temporal/temporal-ui.yaml new file mode 100644 index 00000000..e90b33dc --- /dev/null +++ b/infra/k8s/base/temporal/temporal-ui.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: temporal-ui + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: temporal-ui + template: + metadata: + labels: + app: temporal-ui + spec: + containers: + - name: temporal-ui + image: temporalio/ui:latest + ports: + - containerPort: 8080 + env: + - name: TEMPORAL_ADDRESS + value: "temporal-svc:7233" + - name: TEMPORAL_CORS_ALLOW_ORIGINS + value: "*" +--- +apiVersion: v1 +kind: Service +metadata: + name: temporal-ui-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: temporal-ui + ports: + - port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/infra/k8s/base/temporal/temporal.yaml b/infra/k8s/base/temporal/temporal.yaml new file mode 100644 index 00000000..5c5194e9 --- /dev/null +++ b/infra/k8s/base/temporal/temporal.yaml @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: temporal + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: temporal + template: + metadata: + labels: + app: temporal + spec: + containers: + - name: temporal + image: temporalio/auto-setup:1.29.3 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 7233 + env: + - name: DB + value: postgres12 + - name: POSTGRES_SEEDS + value: postgres + - name: DB_PORT + value: "5432" + - name: POSTGRES_USER + value: "admin" + - name: POSTGRES_PWD + valueFrom: + secretKeyRef: + name: spot-secrets + key: SPRING_DATASOURCE_PASSWORD + - name: DBNAME + value: "spot_temporal" + - name: VISIBILITY_DBNAME + value: "spot_visibility" + - name: SKIP_SCHEMA_SETUP + value: "false" + - name: ENABLE_ES + value: "false" + - name: SKIP_DEFAULT_NAMESPACE_CREATION + value: "false" + - name: LOG_LEVEL + value: "warn" + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" +--- +apiVersion: v1 +kind: Service +metadata: + name: temporal-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: temporal + ports: + - port: 7233 + targetPort: 7233 diff --git a/infra/k8s/kustomization.yaml b/infra/k8s/kustomization.yaml index 20282824..82758521 100644 --- a/infra/k8s/kustomization.yaml +++ b/infra/k8s/kustomization.yaml @@ -16,6 +16,10 @@ resources: - base/kafka/kafka-connect-init.yaml - base/kafka/kafka-ui.yaml + # temporal + - base/temporal/temporal.yaml + - base/temporal/temporal-ui.yaml + # Monitoring & Logging - base/monitoring/loki/loki.yaml - base/monitoring/loki/loki-config.yaml diff --git a/run_k3d.sh b/run_k3d.sh index 20ca930d..be6b3f81 100755 --- a/run_k3d.sh +++ b/run_k3d.sh @@ -164,6 +164,8 @@ deploy_all() { kubectl wait --for=condition=available deployment/kafka -n spot --timeout=180s kubectl wait --for=condition=available deployment/kafka-connect -n spot --timeout=180s kubectl wait --for=condition=available deployment/kafka-ui -n spot --timeout=180s + kubectl wait --for=condition=available deployment/temporal -n spot --timeout=180s + kubectl wait --for=condition=available deployment/temporal-ui -n spot --timeout=180s log_info "Infrastructure deployed successfully!" From fde821addbc70d837170071919f9eee3282fec6f Mon Sep 17 00:00:00 2001 From: first-lounge Date: Thu, 12 Feb 2026 16:29:24 +0900 Subject: [PATCH 06/35] =?UTF-8?q?fix(#321):=20kafak=20ui=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yaml | 10 +++++----- infra/k8s/apps/spot-ingress.yaml | 20 ++++++++++++++++++-- infra/k8s/base/kafka/kafka-ui.yaml | 2 -- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 8cacdc12..1891a746 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -120,11 +120,11 @@ services: networks: - spot-network -# --- Temporal 1--- +# --- Temporal --- temporal: container_name: temporal image: temporalio/auto-setup:1.29.3 - ports: + ports: - "7233:7233" environment: - DB=postgres12 @@ -142,8 +142,8 @@ services: - db networks: - spot-network - - + + temporal-ui: container_name: temporal-ui image: temporalio/ui:latest @@ -156,7 +156,7 @@ services: - temporal networks: - spot-network - + # --- Application Services --- spot-gateway: build: diff --git a/infra/k8s/apps/spot-ingress.yaml b/infra/k8s/apps/spot-ingress.yaml index d9477dc4..625eb8da 100644 --- a/infra/k8s/apps/spot-ingress.yaml +++ b/infra/k8s/apps/spot-ingress.yaml @@ -5,7 +5,6 @@ metadata: namespace: spot annotations: kubernetes.io/ingress.class: nginx -# nginx.ingress.kubernetes.io/rewrite-target: /$1 # 첫 번째 prefix 제거 spec: ingressClassName: nginx rules: @@ -19,10 +18,27 @@ spec: name: spot-gateway port: number: 80 - - path: /kafka-ui + + # kafka + - host: kafka.localhost + http: + paths: + - path: / pathType: Prefix backend: service: name: kafka-ui-svc + port: + number: 80 + + # temporal + - host: temporal.localhost + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: temporal-ui-svc port: number: 80 \ No newline at end of file diff --git a/infra/k8s/base/kafka/kafka-ui.yaml b/infra/k8s/base/kafka/kafka-ui.yaml index c5a4d6ee..f394d3be 100644 --- a/infra/k8s/base/kafka/kafka-ui.yaml +++ b/infra/k8s/base/kafka/kafka-ui.yaml @@ -19,8 +19,6 @@ spec: ports: - containerPort: 8080 env: - - name: SERVER_SERVLET_CONTEXT_PATH - value: '/kafka-ui' - name: KAFKA_CLUSTERS_0_NAME value: local-spot-cluster - name: KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS From 5fa3700722d424629d672b0070b23c225fce55ff Mon Sep 17 00:00:00 2001 From: dbswjd7 Date: Thu, 12 Feb 2026 17:33:03 +0900 Subject: [PATCH 07/35] =?UTF-8?q?feat(#332):=20=EA=B8=B0=EC=A1=B4=20ecs?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20tf=EC=9D=84=20eks=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/terraform/modules/api-gateway/main.tf | 224 ------ .../terraform/modules/api-gateway/outputs.tf | 42 -- .../modules/api-gateway/variables.tf | 64 -- infra/terraform/modules/dns/main.tf | 35 +- infra/terraform/modules/dns/outputs.tf | 14 +- infra/terraform/modules/dns/variables.tf | 21 +- infra/terraform/modules/ecs/codedeploy.tf | 93 --- infra/terraform/modules/ecs/main.tf | 684 ------------------ infra/terraform/modules/ecs/outputs.tf | 57 -- infra/terraform/modules/ecs/variables.tf | 261 ------- infra/terraform/modules/elasticache/main.tf | 2 +- infra/terraform/modules/kafka/main.tf | 2 +- infra/terraform/modules/monitoring/main.tf | 15 +- infra/terraform/modules/monitoring/outputs.tf | 8 +- .../terraform/modules/monitoring/variables.tf | 18 + infra/terraform/modules/network/main.tf | 17 +- infra/terraform/modules/network/variables.tf | 9 + .../modules/parameter-store/outputs.tf | 12 +- infra/terraform/modules/s3/main.tf | 16 + infra/terraform/modules/waf/main.tf | 12 +- infra/terraform/modules/waf/variables.tf | 5 - 21 files changed, 116 insertions(+), 1495 deletions(-) delete mode 100644 infra/terraform/modules/api-gateway/main.tf delete mode 100644 infra/terraform/modules/api-gateway/outputs.tf delete mode 100644 infra/terraform/modules/api-gateway/variables.tf delete mode 100644 infra/terraform/modules/ecs/codedeploy.tf delete mode 100644 infra/terraform/modules/ecs/main.tf delete mode 100644 infra/terraform/modules/ecs/outputs.tf delete mode 100644 infra/terraform/modules/ecs/variables.tf diff --git a/infra/terraform/modules/api-gateway/main.tf b/infra/terraform/modules/api-gateway/main.tf deleted file mode 100644 index 27713dd5..00000000 --- a/infra/terraform/modules/api-gateway/main.tf +++ /dev/null @@ -1,224 +0,0 @@ -# ============================================================================= -# API Gateway (HTTP API) -# ============================================================================= -resource "aws_apigatewayv2_api" "main" { - name = "${var.name_prefix}-api" - protocol_type = "HTTP" - - cors_configuration { - allow_origins = ["*"] - allow_methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"] - allow_headers = ["Content-Type", "Authorization", "X-Requested-With"] - expose_headers = ["X-Request-Id"] - max_age = 3600 - allow_credentials = false - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-api" }) -} - -# ============================================================================= -# Cognito User Pool -# ============================================================================= -resource "aws_cognito_user_pool" "main" { - count = var.enable_cognito ? 1 : 0 - name = var.cognito_user_pool_name != null ? var.cognito_user_pool_name : "${var.name_prefix}-user-pool" - - username_attributes = ["email"] - auto_verified_attributes = ["email"] - - password_policy { - minimum_length = 8 - require_lowercase = true - require_numbers = true - require_symbols = true - require_uppercase = true - temporary_password_validity_days = 7 - } - - email_configuration { - email_sending_account = "COGNITO_DEFAULT" - } - - mfa_configuration = "OPTIONAL" - - software_token_mfa_configuration { - enabled = true - } - - account_recovery_setting { - recovery_mechanism { - name = "verified_email" - priority = 1 - } - } - - schema { - name = "email" - attribute_data_type = "String" - mutable = true - required = true - string_attribute_constraints { - min_length = 1 - max_length = 256 - } - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-user-pool" }) -} - -resource "aws_cognito_user_pool_client" "main" { - count = var.enable_cognito ? 1 : 0 - name = "${var.name_prefix}-api-client" - user_pool_id = aws_cognito_user_pool.main[0].id - - generate_secret = false - allowed_oauth_flows_user_pool_client = true - allowed_oauth_flows = ["code", "implicit"] - allowed_oauth_scopes = ["email", "openid", "profile"] - callback_urls = var.cognito_callback_urls - logout_urls = var.cognito_logout_urls - supported_identity_providers = ["COGNITO"] - - explicit_auth_flows = [ - "ALLOW_REFRESH_TOKEN_AUTH", - "ALLOW_USER_SRP_AUTH", - "ALLOW_USER_PASSWORD_AUTH" - ] - - access_token_validity = 1 - id_token_validity = 1 - refresh_token_validity = 30 - - token_validity_units { - access_token = "hours" - id_token = "hours" - refresh_token = "days" - } -} - -resource "aws_cognito_user_pool_domain" "main" { - count = var.enable_cognito ? 1 : 0 - domain = var.name_prefix - user_pool_id = aws_cognito_user_pool.main[0].id -} - -# ============================================================================= -# JWT Authorizer -# ============================================================================= -resource "aws_apigatewayv2_authorizer" "cognito" { - count = var.enable_cognito ? 1 : 0 - - api_id = aws_apigatewayv2_api.main.id - authorizer_type = "JWT" - name = "${var.name_prefix}-cognito-authorizer" - identity_sources = ["$request.header.Authorization"] - - jwt_configuration { - audience = [aws_cognito_user_pool_client.main[0].id] - issuer = "https://${aws_cognito_user_pool.main[0].endpoint}" - } -} - -# ============================================================================= -# VPC Link -# ============================================================================= -resource "aws_apigatewayv2_vpc_link" "main" { - name = "${var.name_prefix}-vpc-link" - security_group_ids = [var.ecs_security_group_id] - subnet_ids = var.subnet_ids - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-vpc-link" }) -} - -# ============================================================================= -# Integration (VPC Link -> ALB) -# ============================================================================= -resource "aws_apigatewayv2_integration" "main" { - api_id = aws_apigatewayv2_api.main.id - integration_type = "HTTP_PROXY" - integration_method = "ANY" - integration_uri = var.alb_listener_arn - connection_type = "VPC_LINK" - connection_id = aws_apigatewayv2_vpc_link.main.id - - payload_format_version = "1.0" -} - -# ============================================================================= -# Routes (Public - No Auth Required) -# ============================================================================= -resource "aws_apigatewayv2_route" "public" { - for_each = var.enable_cognito ? toset(var.public_routes) : toset([]) - - api_id = aws_apigatewayv2_api.main.id - route_key = "ANY ${each.value}" - target = "integrations/${aws_apigatewayv2_integration.main.id}" -} - -# ============================================================================= -# Routes (Protected - Auth Required) -# ============================================================================= -resource "aws_apigatewayv2_route" "protected" { - for_each = var.enable_cognito ? toset(var.protected_route_patterns) : toset([]) - - api_id = aws_apigatewayv2_api.main.id - route_key = "ANY ${each.value}" - target = "integrations/${aws_apigatewayv2_integration.main.id}" - authorizer_id = aws_apigatewayv2_authorizer.cognito[0].id - authorization_type = "JWT" -} - -# ============================================================================= -# Route (Fallback - When Cognito Disabled) -# ============================================================================= -resource "aws_apigatewayv2_route" "main" { - count = var.enable_cognito ? 0 : 1 - - api_id = aws_apigatewayv2_api.main.id - route_key = "ANY /{proxy+}" - target = "integrations/${aws_apigatewayv2_integration.main.id}" -} - -# ============================================================================= -# Stage -# ============================================================================= -resource "aws_apigatewayv2_stage" "main" { - api_id = aws_apigatewayv2_api.main.id - name = "$default" - auto_deploy = true - - access_log_settings { - destination_arn = aws_cloudwatch_log_group.api_logs.arn - format = jsonencode({ - requestId = "$context.requestId" - ip = "$context.identity.sourceIp" - requestTime = "$context.requestTime" - httpMethod = "$context.httpMethod" - routeKey = "$context.routeKey" - status = "$context.status" - protocol = "$context.protocol" - responseLength = "$context.responseLength" - integrationError = "$context.integrationErrorMessage" - authorizerError = "$context.authorizer.error" - }) - } - - default_route_settings { - detailed_metrics_enabled = true - throttling_burst_limit = 5000 - throttling_rate_limit = 2000 - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-stage" }) -} - -# ============================================================================= -# CloudWatch Log Group for API Gateway -# ============================================================================= -resource "aws_cloudwatch_log_group" "api_logs" { - name = "/aws/apigateway/${var.name_prefix}" - retention_in_days = 30 - - tags = var.common_tags -} diff --git a/infra/terraform/modules/api-gateway/outputs.tf b/infra/terraform/modules/api-gateway/outputs.tf deleted file mode 100644 index a098edd1..00000000 --- a/infra/terraform/modules/api-gateway/outputs.tf +++ /dev/null @@ -1,42 +0,0 @@ -output "api_endpoint" { - description = "API Gateway 엔드포인트 URL" - value = aws_apigatewayv2_api.main.api_endpoint -} - -output "api_id" { - description = "API Gateway ID" - value = aws_apigatewayv2_api.main.id -} - -output "stage_arn" { - description = "API Gateway Stage ARN (WAF 연결용)" - value = aws_apigatewayv2_stage.main.arn -} - -output "execution_arn" { - description = "API Gateway Execution ARN" - value = aws_apigatewayv2_api.main.execution_arn -} - -# ============================================================================= -# Cognito Outputs -# ============================================================================= -output "cognito_user_pool_id" { - description = "Cognito User Pool ID" - value = var.enable_cognito ? aws_cognito_user_pool.main[0].id : null -} - -output "cognito_user_pool_client_id" { - description = "Cognito User Pool Client ID" - value = var.enable_cognito ? aws_cognito_user_pool_client.main[0].id : null -} - -output "cognito_user_pool_endpoint" { - description = "Cognito User Pool Endpoint" - value = var.enable_cognito ? aws_cognito_user_pool.main[0].endpoint : null -} - -output "cognito_domain" { - description = "Cognito Domain URL" - value = var.enable_cognito ? "https://${aws_cognito_user_pool_domain.main[0].domain}.auth.ap-northeast-2.amazoncognito.com" : null -} diff --git a/infra/terraform/modules/api-gateway/variables.tf b/infra/terraform/modules/api-gateway/variables.tf deleted file mode 100644 index 98af3525..00000000 --- a/infra/terraform/modules/api-gateway/variables.tf +++ /dev/null @@ -1,64 +0,0 @@ -variable "name_prefix" { - description = "리소스 네이밍 프리픽스" - type = string -} - -variable "common_tags" { - description = "공통 태그" - type = map(string) - default = {} -} - -variable "subnet_ids" { - description = "VPC Link 서브넷 ID 목록" - type = list(string) -} - -variable "ecs_security_group_id" { - description = "ECS 보안그룹 ID" - type = string -} - -variable "alb_listener_arn" { - description = "ALB Listener ARN" - type = string -} - -# ============================================================================= -# Cognito Settings -# ============================================================================= -variable "enable_cognito" { - description = "Cognito 인증 활성화" - type = bool - default = false -} - -variable "cognito_user_pool_name" { - description = "Cognito User Pool 이름" - type = string - default = null -} - -variable "cognito_callback_urls" { - description = "OAuth 콜백 URL 목록" - type = list(string) - default = ["https://localhost:3000/callback"] -} - -variable "cognito_logout_urls" { - description = "로그아웃 URL 목록" - type = list(string) - default = ["https://localhost:3000"] -} - -variable "public_routes" { - description = "인증이 필요없는 공개 라우트 패턴" - type = list(string) - default = ["/api/auth/*", "/health", "/actuator/health"] -} - -variable "protected_route_patterns" { - description = "보호된 라우트 패턴 목록" - type = list(string) - default = ["/api/*"] -} diff --git a/infra/terraform/modules/dns/main.tf b/infra/terraform/modules/dns/main.tf index b4e09cb3..3b12e790 100644 --- a/infra/terraform/modules/dns/main.tf +++ b/infra/terraform/modules/dns/main.tf @@ -50,41 +50,20 @@ resource "aws_acm_certificate_validation" "main" { validation_record_fqdns = [for record in aws_route53_record.acm_validation : record.fqdn] } + # ============================================================================= -# API Gateway Custom Domain (Optional) +# EKS(ALB) Record # ============================================================================= -resource "aws_apigatewayv2_domain_name" "api" { - count = var.create_api_domain ? 1 : 0 - - domain_name = "api.${var.domain_name}" - - domain_name_configuration { - certificate_arn = aws_acm_certificate_validation.main.certificate_arn - endpoint_type = "REGIONAL" - security_policy = "TLS_1_2" - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-api-domain" }) -} - -resource "aws_apigatewayv2_api_mapping" "api" { - count = var.create_api_domain ? 1 : 0 - - api_id = var.api_gateway_id - domain_name = aws_apigatewayv2_domain_name.api[0].id - stage = "$default" -} - -resource "aws_route53_record" "api" { - count = var.create_api_domain ? 1 : 0 +resource "aws_route53_record" "alb" { + count = var.create_alb_record ? 1 : 0 zone_id = aws_route53_zone.main.zone_id - name = "api.${var.domain_name}" + name = var.alb_record_name type = "A" alias { - name = aws_apigatewayv2_domain_name.api[0].domain_name_configuration[0].target_domain_name - zone_id = aws_apigatewayv2_domain_name.api[0].domain_name_configuration[0].hosted_zone_id + name = var.alb_dns_name + zone_id = var.alb_zone_id evaluate_target_health = false } } diff --git a/infra/terraform/modules/dns/outputs.tf b/infra/terraform/modules/dns/outputs.tf index 7e827d95..c41e6cab 100644 --- a/infra/terraform/modules/dns/outputs.tf +++ b/infra/terraform/modules/dns/outputs.tf @@ -18,12 +18,8 @@ output "certificate_arn" { value = aws_acm_certificate_validation.main.certificate_arn } -output "api_domain" { - description = "API 커스텀 도메인" - value = var.create_api_domain ? "api.${var.domain_name}" : null -} - -output "api_domain_target" { - description = "API Gateway 커스텀 도메인의 target domain name" - value = var.create_api_domain ? aws_apigatewayv2_domain_name.api[0].domain_name_configuration[0].target_domain_name : null -} +# ALB 레코드 FQDN +output "alb_record_fqdn" { + description = "ALB Alias 레코드 FQDN" + value = var.create_alb_record ? aws_route53_record.alb[0].fqdn : null +} \ No newline at end of file diff --git a/infra/terraform/modules/dns/variables.tf b/infra/terraform/modules/dns/variables.tf index 357eb59e..e452ae32 100644 --- a/infra/terraform/modules/dns/variables.tf +++ b/infra/terraform/modules/dns/variables.tf @@ -14,14 +14,27 @@ variable "domain_name" { type = string } -variable "create_api_domain" { - description = "API Gateway 커스텀 도메인 생성 여부" +# ALB alias record 생성 여부 +variable "create_alb_record" { + description = "ALB(Route53 Alias) 레코드 생성 여부" type = bool default = true } -variable "api_gateway_id" { - description = "API Gateway ID" +variable "alb_record_name" { + description = "생성할 레코드 이름" + type = string + default = "" +} + +variable "alb_dns_name" { + description = "ALB DNS name (ex: xxx.ap-northeast-2.elb.amazonaws.com)" + type = string + default = "" +} + +variable "alb_zone_id" { + description = "ALB Hosted Zone ID" type = string default = "" } diff --git a/infra/terraform/modules/ecs/codedeploy.tf b/infra/terraform/modules/ecs/codedeploy.tf deleted file mode 100644 index 93757b8f..00000000 --- a/infra/terraform/modules/ecs/codedeploy.tf +++ /dev/null @@ -1,93 +0,0 @@ -# ============================================================================= -# CodeDeploy Application (for Blue/Green ECS Deployment) -# ============================================================================= -resource "aws_codedeploy_app" "main" { - count = var.enable_blue_green ? 1 : 0 - compute_platform = "ECS" - name = "${var.name_prefix}-ecs-app" - - tags = var.common_tags -} - -# ============================================================================= -# CodeDeploy IAM Role -# ============================================================================= -resource "aws_iam_role" "codedeploy" { - count = var.enable_blue_green ? 1 : 0 - name = "${var.name_prefix}-codedeploy-role" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [{ - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "codedeploy.amazonaws.com" - } - }] - }) - - tags = var.common_tags -} - -resource "aws_iam_role_policy_attachment" "codedeploy" { - count = var.enable_blue_green ? 1 : 0 - role = aws_iam_role.codedeploy[0].name - policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS" -} - -# ============================================================================= -# CodeDeploy Deployment Groups (per service) -# ============================================================================= -resource "aws_codedeploy_deployment_group" "services" { - for_each = var.enable_blue_green ? local.active_services : {} - - app_name = aws_codedeploy_app.main[0].name - deployment_group_name = "${var.name_prefix}-${each.key}-dg" - deployment_config_name = var.deployment_config - service_role_arn = aws_iam_role.codedeploy[0].arn - - auto_rollback_configuration { - enabled = true - events = ["DEPLOYMENT_FAILURE"] - } - - blue_green_deployment_config { - deployment_ready_option { - action_on_timeout = "CONTINUE_DEPLOYMENT" - } - - terminate_blue_instances_on_deployment_success { - action = "TERMINATE" - termination_wait_time_in_minutes = var.termination_wait_time - } - } - - deployment_style { - deployment_option = "WITH_TRAFFIC_CONTROL" - deployment_type = "BLUE_GREEN" - } - - ecs_service { - cluster_name = aws_ecs_cluster.main.name - service_name = aws_ecs_service.services[each.key].name - } - - load_balancer_info { - target_group_pair_info { - prod_traffic_route { - listener_arns = [var.alb_listener_arn] - } - - target_group { - name = var.target_group_names[each.key] - } - - target_group { - name = lookup(var.target_group_names, "${each.key}-green", "${var.name_prefix}-${each.key}-tg-g") - } - } - } - - tags = merge(var.common_tags, { Service = each.key }) -} diff --git a/infra/terraform/modules/ecs/main.tf b/infra/terraform/modules/ecs/main.tf deleted file mode 100644 index ed6cfbf7..00000000 --- a/infra/terraform/modules/ecs/main.tf +++ /dev/null @@ -1,684 +0,0 @@ -# ============================================================================= -# Local Variables -# ============================================================================= -locals { - # Gateway 및 excluded_services에 포함된 서비스 필터링 - active_services = { - for k, v in var.services : k => v - if !contains(var.excluded_services, k) - } -} - -# ============================================================================= -# Cloud Map (Service Discovery Namespace) -# ============================================================================= -resource "aws_service_discovery_private_dns_namespace" "main" { - name = "${var.project}.local" - vpc = var.vpc_id - - tags = merge(var.common_tags, { Name = "${var.project}.local" }) -} - -# ============================================================================= -# Cloud Map Services (Multiple) - Only when Service Connect is disabled -# ============================================================================= -resource "aws_service_discovery_service" "services" { - for_each = var.enable_service_connect ? {} : var.services - - name = each.key - - dns_config { - namespace_id = aws_service_discovery_private_dns_namespace.main.id - routing_policy = "MULTIVALUE" - - dns_records { - ttl = 10 - type = "A" - } - dns_records { - ttl = 10 - type = "SRV" - } - } - - health_check_custom_config { - failure_threshold = 1 - } - - tags = merge(var.common_tags, { Service = each.key }) -} - -# ============================================================================= -# ECS Cluster (Single shared cluster) -# ============================================================================= -resource "aws_ecs_cluster" "main" { - name = "${var.name_prefix}-cluster" - - service_connect_defaults { - namespace = aws_service_discovery_private_dns_namespace.main.arn - } - - setting { - name = "containerInsights" - value = "enabled" - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-cluster" }) -} - -resource "aws_ecs_cluster_capacity_providers" "main" { - cluster_name = aws_ecs_cluster.main.name - - capacity_providers = ["FARGATE", "FARGATE_SPOT"] - - default_capacity_provider_strategy { - base = 1 - weight = 100 - capacity_provider = "FARGATE" - } -} - -# ============================================================================= -# MSA Security Group (Shared by all services) -# ============================================================================= -resource "aws_security_group" "msa_sg" { - name = "${var.name_prefix}-msa-sg" - description = "Security group for MSA services" - vpc_id = var.vpc_id - - # ALB에서 들어오는 트래픽 허용 (Gateway: 8080) - ingress { - description = "Traffic from ALB" - from_port = 8080 - to_port = 8080 - protocol = "tcp" - security_groups = [var.alb_security_group_id] - } - - # Service Connect를 위한 자기 참조 규칙 (서비스 간 통신: 8080-8084) - ingress { - description = "Inter-service communication" - from_port = 8080 - to_port = 8084 - protocol = "tcp" - self = true - } - - # Service Connect proxy port - ingress { - description = "Service Connect proxy" - from_port = 15000 - to_port = 15001 - protocol = "tcp" - self = true - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-msa-sg" }) -} - -# ============================================================================= -# IAM Role for ECS Task Execution -# ============================================================================= -resource "aws_iam_role" "ecs_task_execution_role" { - name = "${var.name_prefix}-ecs-task-execution-role" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [{ - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { Service = "ecs-tasks.amazonaws.com" } - }] - }) - - tags = var.common_tags -} - -resource "aws_iam_role_policy_attachment" "ecs_task_execution_role_policy" { - role = aws_iam_role.ecs_task_execution_role.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" -} - -# ============================================================================= -# SSM Parameter Store 읽기 권한 (Secrets 주입용) -# ============================================================================= -resource "aws_iam_role_policy" "ecs_task_execution_ssm" { - name = "${var.name_prefix}-ecs-ssm-policy" - role = aws_iam_role.ecs_task_execution_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "ssm:GetParameters", - "ssm:GetParameter" - ] - Resource = "arn:aws:ssm:${var.region}:*:parameter/${var.project}/${var.environment}/*" - }, - { - Effect = "Allow" - Action = [ - "kms:Decrypt" - ] - Resource = "*" - Condition = { - StringEquals = { - "kms:ViaService" = "ssm.${var.region}.amazonaws.com" - } - } - } - ] - }) -} - -# ============================================================================= -# IAM Role for ECS Task (Application level) -# ============================================================================= -resource "aws_iam_role" "ecs_task_role" { - name = "${var.name_prefix}-ecs-task-role" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [{ - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { Service = "ecs-tasks.amazonaws.com" } - }] - }) - - tags = var.common_tags -} - -# Service Connect requires CloudWatch permissions -resource "aws_iam_role_policy" "ecs_service_connect" { - name = "${var.name_prefix}-service-connect-policy" - role = aws_iam_role.ecs_task_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - Resource = "*" - } - ] - }) -} - -# ============================================================================= -# CloudWatch Log Groups (per service) -# ============================================================================= -resource "aws_cloudwatch_log_group" "services" { - for_each = var.services - - name = "/ecs/${var.project}-${each.key}" - retention_in_days = var.log_retention_days - - tags = merge(var.common_tags, { Service = each.key }) -} - -# ============================================================================= -# ECS Task Definitions (per service) -# ============================================================================= -resource "aws_ecs_task_definition" "services" { - for_each = var.services - - family = "${var.project}-${each.key}-task" - network_mode = "awsvpc" - requires_compatibilities = ["FARGATE"] - cpu = each.value.cpu - memory = each.value.memory - execution_role_arn = aws_iam_role.ecs_task_execution_role.arn - task_role_arn = aws_iam_role.ecs_task_role.arn - - container_definitions = jsonencode([ - { - name = "${var.project}-${each.key}-container" - image = "${var.ecr_repository_urls[each.key]}:latest" - essential = true - - portMappings = [{ - name = each.key - containerPort = each.value.container_port - hostPort = each.value.container_port - protocol = "tcp" - appProtocol = "http" - }] - - environment = concat( - # 공통 환경 변수 - [ - { - name = "SPRING_PROFILES_ACTIVE" - value = var.environment - }, - { - name = "SPRING_DATA_REDIS_HOST" - value = var.redis_endpoint - }, - { - name = "SPRING_DATA_REDIS_PORT" - value = "6379" - } - ], - # Kafka 환경 변수 (gateway 제외) - each.key != "gateway" && var.kafka_bootstrap_servers != "" ? [ - { - name = "SPRING_KAFKA_BOOTSTRAP_SERVERS" - value = var.kafka_bootstrap_servers - }, - { - name = "SPRING_KAFKA_CONSUMER_GROUP_ID" - value = "${var.project}-${each.key}" - }, - { - name = "SPRING_KAFKA_CONSUMER_AUTO_OFFSET_RESET" - value = "earliest" - } - ] : [], - # 백엔드 서비스 전용 (gateway 제외) - DB, JPA, JWT 설정 - each.key != "gateway" ? [ - { - name = "SPRING_DATASOURCE_URL" - value = "jdbc:postgresql://${var.db_endpoint}/${var.db_name}?currentSchema=${lookup(each.value.environment_vars, "DB_SCHEMA", each.key)}" - }, - { - name = "SPRING_DATASOURCE_USERNAME" - value = var.db_username - }, - { - name = "SPRING_JPA_HIBERNATE_DDL_AUTO" - value = "update" - }, - { - name = "SPRING_JPA_SHOW_SQL" - value = "false" - }, - { - name = "SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT" - value = "org.hibernate.dialect.PostgreSQLDialect" - }, - { - name = "SPRING_JWT_EXPIRE_MS" - value = tostring(var.jwt_expire_ms) - }, - { - name = "SPRING_SECURITY_REFRESH_TOKEN_EXPIRE_DAYS" - value = tostring(var.refresh_token_expire_days) - }, - { - name = "SERVICE_ACTIVE_REGIONS" - value = var.service_active_regions - } - ] : [], - # Service Discovery 환경 변수 (Feign Client URLs) - each.key != "gateway" ? [ - { - name = "FEIGN_ORDER_URL" - value = "http://order.${var.project}.local:${var.services["order"].container_port}" - }, - { - name = "FEIGN_PAYMENT_URL" - value = "http://payment.${var.project}.local:${var.services["payment"].container_port}" - }, - { - name = "FEIGN_STORE_URL" - value = "http://store.${var.project}.local:${var.services["store"].container_port}" - }, - { - name = "FEIGN_USER_URL" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - } - ] : [], - # Mail 설정 (user 서비스용) - each.key == "user" ? [ - { - name = "SPRING_MAIL_HOST" - value = var.mail_host - }, - { - name = "SPRING_MAIL_PORT" - value = tostring(var.mail_port) - }, - { - name = "SPRING_MAIL_USERNAME" - value = var.mail_username - }, - { - name = "SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH" - value = "true" - }, - { - name = "SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE" - value = "true" - } - ] : [], - # Toss 결제 설정 (payment 서비스용) - each.key == "payment" ? [ - { - name = "TOSS_PAYMENTS_BASE_URL" - value = var.toss_base_url - }, - { - name = "TOSS_PAYMENTS_CUSTOMER_KEY" - value = var.toss_customer_key - } - ] : [], - # Gateway 전용 설정 - Spring Cloud Gateway 라우트 (WebFlux 버전용 새 property 이름) - each.key == "gateway" ? [ - # User Service - Auth 관련 - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_0_ID" - value = "user-login" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_0_URI" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_0_PREDICATES_0" - value = "Path=/api/login" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_1_ID" - value = "user-join" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_1_URI" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_1_PREDICATES_0" - value = "Path=/api/join" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_2_ID" - value = "user-auth" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_2_URI" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_2_PREDICATES_0" - value = "Path=/api/auth/**" - }, - # User Service - Users & Admin - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_3_ID" - value = "user-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_3_URI" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_3_PREDICATES_0" - value = "Path=/api/users/**" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_4_ID" - value = "admin-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_4_URI" - value = "http://user.${var.project}.local:${var.services["user"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_4_PREDICATES_0" - value = "Path=/api/admin/**" - }, - # Store Service - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_5_ID" - value = "store-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_5_URI" - value = "http://store.${var.project}.local:${var.services["store"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_5_PREDICATES_0" - value = "Path=/api/stores/**" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_6_ID" - value = "category-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_6_URI" - value = "http://store.${var.project}.local:${var.services["store"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_6_PREDICATES_0" - value = "Path=/api/categories/**" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_7_ID" - value = "review-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_7_URI" - value = "http://store.${var.project}.local:${var.services["store"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_7_PREDICATES_0" - value = "Path=/api/reviews/**" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_8_ID" - value = "menu-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_8_URI" - value = "http://store.${var.project}.local:${var.services["store"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_8_PREDICATES_0" - value = "Path=/api/menus/**" - }, - # Order Service - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_9_ID" - value = "order-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_9_URI" - value = "http://order.${var.project}.local:${var.services["order"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_9_PREDICATES_0" - value = "Path=/api/orders/**" - }, - # Payment Service - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_10_ID" - value = "payment-service" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_10_URI" - value = "http://payment.${var.project}.local:${var.services["payment"].container_port}" - }, - { - name = "SPRING_CLOUD_GATEWAY_SERVER_WEBFLUX_ROUTES_10_PREDICATES_0" - value = "Path=/api/payments/**" - }, - # Actuator 설정 (새 property 이름) - { - name = "MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE" - value = "health,info,gateway" - }, - { - name = "MANAGEMENT_ENDPOINT_GATEWAY_ACCESS" - value = "unrestricted" - }, - # 디버깅용 로깅 - { - name = "LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_CLOUD_GATEWAY" - value = "DEBUG" - } - ] : [], - # 서비스별 커스텀 환경 변수 - [for k, v in each.value.environment_vars : { - name = k - value = v - }] - ) - - # ============================================================= - # Secrets (Parameter Store에서 주입) - # ============================================================= - secrets = concat( - # 백엔드 서비스 (gateway 제외) - DB 비밀번호, JWT 시크릿 - each.key != "gateway" ? [ - { - name = "SPRING_DATASOURCE_PASSWORD" - valueFrom = var.parameter_arns.db_password - }, - { - name = "SPRING_JWT_SECRET" - valueFrom = var.parameter_arns.jwt_secret - } - ] : [], - # Mail 비밀번호 (user 서비스) - each.key == "user" && var.parameter_arns.mail_password != null ? [ - { - name = "SPRING_MAIL_PASSWORD" - valueFrom = var.parameter_arns.mail_password - } - ] : [], - # Toss 시크릿 키 (payment 서비스) - each.key == "payment" && var.parameter_arns.toss_secret_key != null ? [ - { - name = "TOSS_PAYMENTS_SECRET_KEY" - valueFrom = var.parameter_arns.toss_secret_key - } - ] : [] - ) - - logConfiguration = { - logDriver = "awslogs" - options = { - "awslogs-group" = aws_cloudwatch_log_group.services[each.key].name - "awslogs-region" = var.region - "awslogs-stream-prefix" = "ecs" - } - } - - healthCheck = { - command = ["CMD-SHELL", "curl -f http://localhost:${each.value.container_port}${each.value.health_check_path} || exit 1"] - interval = 30 - timeout = 5 - retries = 3 - startPeriod = 60 - } - } - ]) - - tags = merge(var.common_tags, { Service = each.key }) -} - -# ============================================================================= -# ECS Services (per service) - Active Services Only -# ============================================================================= -resource "aws_ecs_service" "services" { - for_each = local.active_services - - name = "${var.project}-${each.key}-service" - cluster = aws_ecs_cluster.main.id - task_definition = aws_ecs_task_definition.services[each.key].arn - desired_count = var.standby_mode ? 0 : each.value.desired_count - launch_type = "FARGATE" - - # 모든 active 서비스를 ALB에 연결 - dynamic "load_balancer" { - for_each = contains(keys(var.target_group_arns), each.key) ? [1] : [] - content { - target_group_arn = var.target_group_arns[each.key] - container_name = "${var.project}-${each.key}-container" - container_port = each.value.container_port - } - } - - network_configuration { - subnets = var.subnet_ids - security_groups = [aws_security_group.msa_sg.id] - assign_public_ip = var.assign_public_ip - } - - # Blue/Green 배포 컨트롤러 - dynamic "deployment_controller" { - for_each = var.enable_blue_green ? [1] : [] - content { - type = "CODE_DEPLOY" - } - } - - # 기본 롤링 배포 설정 (Blue/Green 비활성화시) - dynamic "deployment_circuit_breaker" { - for_each = var.enable_blue_green ? [] : [1] - content { - enable = true - rollback = true - } - } - - # Service Connect Configuration - dynamic "service_connect_configuration" { - for_each = var.enable_service_connect ? [1] : [] - content { - enabled = true - namespace = aws_service_discovery_private_dns_namespace.main.arn - - service { - port_name = each.key - discovery_name = each.key - - client_alias { - port = each.value.container_port - dns_name = "${each.key}.${var.project}.local" - } - } - - log_configuration { - log_driver = "awslogs" - options = { - "awslogs-group" = aws_cloudwatch_log_group.services[each.key].name - "awslogs-region" = var.region - "awslogs-stream-prefix" = "service-connect" - } - } - } - } - - # Service Discovery Registration (only when Service Connect is disabled) - dynamic "service_registries" { - for_each = var.enable_service_connect ? [] : [1] - content { - registry_arn = aws_service_discovery_service.services[each.key].arn - container_name = "${var.project}-${each.key}-container" - container_port = each.value.container_port - } - } - - depends_on = [var.alb_listener_arn] - - tags = merge(var.common_tags, { Service = each.key }) - - lifecycle { - ignore_changes = var.enable_blue_green ? [task_definition, load_balancer] : [] - } -} diff --git a/infra/terraform/modules/ecs/outputs.tf b/infra/terraform/modules/ecs/outputs.tf deleted file mode 100644 index fc8710db..00000000 --- a/infra/terraform/modules/ecs/outputs.tf +++ /dev/null @@ -1,57 +0,0 @@ -output "cluster_name" { - description = "ECS 클러스터 이름" - value = aws_ecs_cluster.main.name -} - -output "cluster_arn" { - description = "ECS 클러스터 ARN" - value = aws_ecs_cluster.main.arn -} - -output "service_names" { - description = "ECS 서비스 이름 맵" - value = { for k, v in aws_ecs_service.services : k => v.name } -} - -output "service_arns" { - description = "ECS 서비스 ARN 맵" - value = { for k, v in aws_ecs_service.services : k => v.id } -} - -output "security_group_id" { - description = "MSA 보안그룹 ID" - value = aws_security_group.msa_sg.id -} - -output "task_definition_arns" { - description = "Task Definition ARN 맵" - value = { for k, v in aws_ecs_task_definition.services : k => v.arn } -} - -output "cloudwatch_log_groups" { - description = "CloudWatch Log Group 이름 맵" - value = { for k, v in aws_cloudwatch_log_group.services : k => v.name } -} - -output "service_discovery_namespace_id" { - description = "Service Discovery Namespace ID" - value = aws_service_discovery_private_dns_namespace.main.id -} - -output "service_discovery_namespace_arn" { - description = "Service Discovery Namespace ARN" - value = aws_service_discovery_private_dns_namespace.main.arn -} - -# ============================================================================= -# CodeDeploy Outputs -# ============================================================================= -output "codedeploy_app_name" { - description = "CodeDeploy Application 이름" - value = var.enable_blue_green ? aws_codedeploy_app.main[0].name : null -} - -output "codedeploy_deployment_group_names" { - description = "CodeDeploy Deployment Group 이름 맵" - value = var.enable_blue_green ? { for k, v in aws_codedeploy_deployment_group.services : k => v.deployment_group_name } : {} -} diff --git a/infra/terraform/modules/ecs/variables.tf b/infra/terraform/modules/ecs/variables.tf deleted file mode 100644 index 3ecbf149..00000000 --- a/infra/terraform/modules/ecs/variables.tf +++ /dev/null @@ -1,261 +0,0 @@ -# ============================================================================= -# Project Settings -# ============================================================================= -variable "project" { - description = "프로젝트 이름" - type = string -} - -variable "environment" { - description = "환경 (dev, prod)" - type = string - default = "dev" -} - -variable "name_prefix" { - description = "리소스 네이밍 프리픽스" - type = string -} - -variable "common_tags" { - description = "공통 태그" - type = map(string) - default = {} -} - -variable "region" { - description = "AWS 리전" - type = string -} - -# ============================================================================= -# Network Settings -# ============================================================================= -variable "vpc_id" { - description = "VPC ID" - type = string -} - -variable "subnet_ids" { - description = "ECS 서비스 서브넷 ID 목록" - type = list(string) -} - -variable "assign_public_ip" { - description = "Public IP 할당 여부" - type = bool - default = true -} - -# ============================================================================= -# ALB Integration -# ============================================================================= -variable "alb_security_group_id" { - description = "ALB 보안그룹 ID" - type = string -} - -variable "target_group_arns" { - description = "ALB Target Group ARN 맵" - type = map(string) -} - -variable "alb_listener_arn" { - description = "ALB Listener ARN (의존성용)" - type = string -} - -# ============================================================================= -# ECR Integration -# ============================================================================= -variable "ecr_repository_urls" { - description = "ECR 저장소 URL 맵" - type = map(string) -} - -# ============================================================================= -# Services Configuration -# ============================================================================= -variable "services" { - description = "서비스 구성 맵" - type = map(object({ - container_port = number - cpu = string - memory = string - desired_count = number - health_check_path = string - path_patterns = list(string) - priority = number - environment_vars = map(string) - })) -} - -variable "enable_service_connect" { - description = "ECS Service Connect 활성화 여부" - type = bool - default = true -} - -variable "log_retention_days" { - description = "로그 보관 일수" - type = number - default = 30 -} - -# ============================================================================= -# Parameter Store ARNs (Secrets 주입용) -# ============================================================================= -variable "parameter_arns" { - description = "Parameter Store ARN 맵" - type = object({ - db_password = string - jwt_secret = string - mail_password = optional(string) - toss_secret_key = optional(string) - }) -} - -# ============================================================================= -# Database Settings -# ============================================================================= -variable "db_endpoint" { - description = "RDS 엔드포인트" - type = string -} - -variable "db_name" { - description = "데이터베이스 이름" - type = string -} - -variable "db_username" { - description = "데이터베이스 사용자 이름" - type = string - sensitive = true -} - -# ============================================================================= -# Redis Settings -# ============================================================================= -variable "redis_endpoint" { - description = "Redis 엔드포인트" - type = string - default = "" -} - -# ============================================================================= -# Kafka Settings -# ============================================================================= -variable "kafka_bootstrap_servers" { - description = "Kafka Bootstrap Servers" - type = string - default = "" -} - -# ============================================================================= -# JWT Settings -# ============================================================================= -variable "jwt_expire_ms" { - description = "JWT 만료 시간 (밀리초)" - type = number - default = 3600000 -} - -variable "refresh_token_expire_days" { - description = "리프레시 토큰 만료 일수" - type = number - default = 14 -} - -# ============================================================================= -# Mail Settings -# ============================================================================= -variable "mail_host" { - description = "SMTP 호스트" - type = string - default = "smtp.gmail.com" -} - -variable "mail_port" { - description = "SMTP 포트" - type = number - default = 587 -} - -variable "mail_username" { - description = "SMTP 사용자 이름" - type = string - default = "" -} - -# ============================================================================= -# Toss Payments Settings -# ============================================================================= -variable "toss_base_url" { - description = "Toss Payments API URL" - type = string - default = "https://api.tosspayments.com" -} - -variable "toss_customer_key" { - description = "Toss Payments 고객 키" - type = string - default = "customer_1" -} - -# ============================================================================= -# Service Settings -# ============================================================================= -variable "service_active_regions" { - description = "서비스 활성 지역" - type = string - default = "종로구" -} - -# ============================================================================= -# Standby Mode (비용 절감) -# ============================================================================= -variable "standby_mode" { - description = "스탠바이 모드 (true면 모든 서비스 desired_count = 0)" - type = bool - default = false -} - -# ============================================================================= -# Blue/Green Deployment -# ============================================================================= -variable "enable_blue_green" { - description = "Blue/Green 배포 활성화 (CodeDeploy)" - type = bool - default = false -} - -variable "excluded_services" { - description = "배포에서 제외할 서비스 목록 (예: gateway)" - type = list(string) - default = [] -} - -variable "target_group_names" { - description = "ALB Target Group 이름 맵" - type = map(string) - default = {} -} - -variable "green_target_group_arns" { - description = "Green Target Group ARN 맵 (Blue/Green용)" - type = map(string) - default = {} -} - -variable "deployment_config" { - description = "CodeDeploy 배포 구성" - type = string - default = "CodeDeployDefault.ECSAllAtOnce" -} - -variable "termination_wait_time" { - description = "이전 태스크 종료 대기 시간 (분)" - type = number - default = 5 -} diff --git a/infra/terraform/modules/elasticache/main.tf b/infra/terraform/modules/elasticache/main.tf index 67ab2806..74425a8b 100644 --- a/infra/terraform/modules/elasticache/main.tf +++ b/infra/terraform/modules/elasticache/main.tf @@ -21,7 +21,7 @@ resource "aws_security_group" "redis" { vpc_id = var.vpc_id ingress { - description = "Redis from ECS" + description = "Redis from EKS nodes" from_port = 6379 to_port = 6379 protocol = "tcp" diff --git a/infra/terraform/modules/kafka/main.tf b/infra/terraform/modules/kafka/main.tf index 0bc11bb5..61cb1534 100644 --- a/infra/terraform/modules/kafka/main.tf +++ b/infra/terraform/modules/kafka/main.tf @@ -27,7 +27,7 @@ resource "aws_security_group" "kafka" { name = "${var.name_prefix}-kafka-sg" vpc_id = var.vpc_id - # Kafka 클라이언트 포트 (ECS에서 접근) + # Kafka 클라이언트 포트 (EKS에서 접근) ingress { from_port = local.kafka_port to_port = local.kafka_port diff --git a/infra/terraform/modules/monitoring/main.tf b/infra/terraform/modules/monitoring/main.tf index 8a83e87f..c9a86536 100644 --- a/infra/terraform/modules/monitoring/main.tf +++ b/infra/terraform/modules/monitoring/main.tf @@ -21,7 +21,12 @@ resource "aws_sns_topic_subscription" "email" { # ----------------------------------------------------------------------------- # ECS Alarms # ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ECS Alarms (Optional) +# ----------------------------------------------------------------------------- resource "aws_cloudwatch_metric_alarm" "ecs_cpu_high" { + count = var.enable_ecs_alarms ? 1 : 0 + alarm_name = "${var.name_prefix}-ecs-cpu-high" comparison_operator = "GreaterThanThreshold" evaluation_periods = 2 @@ -43,6 +48,8 @@ resource "aws_cloudwatch_metric_alarm" "ecs_cpu_high" { } resource "aws_cloudwatch_metric_alarm" "ecs_memory_high" { + count = var.enable_ecs_alarms ? 1 : 0 + alarm_name = "${var.name_prefix}-ecs-memory-high" comparison_operator = "GreaterThanThreshold" evaluation_periods = 2 @@ -63,6 +70,7 @@ resource "aws_cloudwatch_metric_alarm" "ecs_memory_high" { tags = var.common_tags } + # ----------------------------------------------------------------------------- # RDS Alarms # ----------------------------------------------------------------------------- @@ -127,9 +135,11 @@ resource "aws_cloudwatch_metric_alarm" "rds_storage_low" { } # ----------------------------------------------------------------------------- -# ALB Alarms +# ALB Alarms (Optional) # ----------------------------------------------------------------------------- resource "aws_cloudwatch_metric_alarm" "alb_5xx_errors" { + count = var.enable_alb_alarms ? 1 : 0 + alarm_name = "${var.name_prefix}-alb-5xx-errors" comparison_operator = "GreaterThanThreshold" evaluation_periods = 2 @@ -151,6 +161,8 @@ resource "aws_cloudwatch_metric_alarm" "alb_5xx_errors" { } resource "aws_cloudwatch_metric_alarm" "alb_response_time" { + count = var.enable_alb_alarms ? 1 : 0 + alarm_name = "${var.name_prefix}-alb-response-time" comparison_operator = "GreaterThanThreshold" evaluation_periods = 2 @@ -170,6 +182,7 @@ resource "aws_cloudwatch_metric_alarm" "alb_response_time" { tags = var.common_tags } + # ----------------------------------------------------------------------------- # ElastiCache (Redis) Alarms # ----------------------------------------------------------------------------- diff --git a/infra/terraform/modules/monitoring/outputs.tf b/infra/terraform/modules/monitoring/outputs.tf index e315af4e..5b5e3332 100644 --- a/infra/terraform/modules/monitoring/outputs.tf +++ b/infra/terraform/modules/monitoring/outputs.tf @@ -14,12 +14,12 @@ output "sns_topic_name" { output "alarm_arns" { description = "생성된 CloudWatch Alarm ARN 목록" value = { - ecs_cpu = aws_cloudwatch_metric_alarm.ecs_cpu_high.arn - ecs_memory = aws_cloudwatch_metric_alarm.ecs_memory_high.arn + ecs_cpu = var.enable_ecs_alarms ? aws_cloudwatch_metric_alarm.ecs_cpu_high[0].arn : null + ecs_memory = var.enable_ecs_alarms ? aws_cloudwatch_metric_alarm.ecs_memory_high[0].arn : null rds_cpu = aws_cloudwatch_metric_alarm.rds_cpu_high.arn rds_connections = aws_cloudwatch_metric_alarm.rds_connections_high.arn rds_storage = aws_cloudwatch_metric_alarm.rds_storage_low.arn - alb_5xx = aws_cloudwatch_metric_alarm.alb_5xx_errors.arn - alb_response = aws_cloudwatch_metric_alarm.alb_response_time.arn + alb_5xx = var.enable_alb_alarms ? aws_cloudwatch_metric_alarm.alb_5xx_errors[0].arn : null + alb_response = var.enable_alb_alarms ? aws_cloudwatch_metric_alarm.alb_response_time[0].arn : null } } diff --git a/infra/terraform/modules/monitoring/variables.tf b/infra/terraform/modules/monitoring/variables.tf index c153416e..ab4e813e 100644 --- a/infra/terraform/modules/monitoring/variables.tf +++ b/infra/terraform/modules/monitoring/variables.tf @@ -26,11 +26,13 @@ variable "alert_email" { variable "ecs_cluster_name" { description = "ECS 클러스터 이름" type = string + default = "" } variable "ecs_service_name" { description = "ECS 서비스 이름" type = string + default = "" } variable "ecs_cpu_threshold" { @@ -77,6 +79,7 @@ variable "rds_storage_threshold_bytes" { variable "alb_arn_suffix" { description = "ALB ARN suffix (app/xxx/xxx 형식)" type = string + default = "" } variable "alb_5xx_threshold" { @@ -111,3 +114,18 @@ variable "redis_memory_threshold" { type = number default = 80 } + +# ============================================================================= +# Monitoring 옵션 +# ============================================================================= +variable "enable_ecs_alarms" { + description = "ECS 알람 생성 여부 (EKS 전환 시 보통 false)" + type = bool + default = false +} + +variable "enable_alb_alarms" { + description = "ALB 알람 생성 여부 (ALB ARN suffix가 확정된 경우에만 true)" + type = bool + default = false +} diff --git a/infra/terraform/modules/network/main.tf b/infra/terraform/modules/network/main.tf index 0831a44f..87044db9 100644 --- a/infra/terraform/modules/network/main.tf +++ b/infra/terraform/modules/network/main.tf @@ -20,6 +20,9 @@ resource "aws_subnet" "public_a" { tags = merge(var.common_tags, { Name = "${var.name_prefix}-public-a" Tier = "public" + + "kubernetes.io/cluster/${var.eks_cluster_name}" = "shared" + "kubernetes.io/role/elb" = "1" }) } @@ -32,6 +35,9 @@ resource "aws_subnet" "public_c" { tags = merge(var.common_tags, { Name = "${var.name_prefix}-public-c" Tier = "public" + + "kubernetes.io/cluster/${var.eks_cluster_name}" = "shared" + "kubernetes.io/role/elb" = "1" }) } @@ -46,6 +52,9 @@ resource "aws_subnet" "private_a" { tags = merge(var.common_tags, { Name = "${var.name_prefix}-private-a" Tier = "private" + + "kubernetes.io/cluster/${var.eks_cluster_name}" = "shared" + "kubernetes.io/role/internal-elb" = "1" }) } @@ -54,7 +63,13 @@ resource "aws_subnet" "private_c" { cidr_block = var.private_subnet_cidrs["c"] availability_zone = var.availability_zones["c"] - tags = merge(var.common_tags, { Name = "${var.name_prefix}-private-c" }) + tags = merge(var.common_tags, { + Name = "${var.name_prefix}-private-c" + Tier = "private" + + "kubernetes.io/cluster/${var.eks_cluster_name}" = "shared" + "kubernetes.io/role/internal-elb" = "1" + }) } # ============================================================================= diff --git a/infra/terraform/modules/network/variables.tf b/infra/terraform/modules/network/variables.tf index fba795a0..0b0011c8 100644 --- a/infra/terraform/modules/network/variables.tf +++ b/infra/terraform/modules/network/variables.tf @@ -49,3 +49,12 @@ variable "single_nat_gateway" { type = bool default = true } + +# ============================================================================= +# EKS Cluster Name +# ============================================================================= +variable "eks_cluster_name" { + description = "EKS 클러스터 이름 (Subnet tag 용도)" + type = string + default = "spot-eks" +} diff --git a/infra/terraform/modules/parameter-store/outputs.tf b/infra/terraform/modules/parameter-store/outputs.tf index a1d19c00..00c777f0 100644 --- a/infra/terraform/modules/parameter-store/outputs.tf +++ b/infra/terraform/modules/parameter-store/outputs.tf @@ -3,7 +3,7 @@ # ============================================================================= # ============================================================================= -# Parameter ARNs (ECS Task Definition secrets 블록에서 사용) +# Parameter ARNs (EKS에서는 IRSA로 Pod에서 SSM GetParameter 권한 부여 후 사용) # ============================================================================= output "db_password_arn" { description = "DB Password Parameter ARN" @@ -39,14 +39,14 @@ output "redis_endpoint_arn" { # All Parameter ARNs (IAM Policy용) # ============================================================================= output "all_parameter_arns" { - description = "모든 Parameter ARN 목록 (IAM Policy용)" + description = "모든 Parameter ARN 목록 (EKS IRSA/IAM Policy용)" value = compact([ aws_ssm_parameter.db_password.arn, aws_ssm_parameter.jwt_secret.arn, - var.mail_password != "" ? aws_ssm_parameter.mail_password[0].arn : null, - var.toss_secret_key != "" ? aws_ssm_parameter.toss_secret_key[0].arn : null, + var.mail_password != "" ? aws_ssm_parameter.mail_password[0].arn : null, + var.toss_secret_key != "" ? aws_ssm_parameter.toss_secret_key[0].arn : null, aws_ssm_parameter.db_endpoint.arn, - var.redis_endpoint != "" ? aws_ssm_parameter.redis_endpoint[0].arn : null, + var.redis_endpoint != "" ? aws_ssm_parameter.redis_endpoint[0].arn : null, ]) } @@ -54,6 +54,6 @@ output "all_parameter_arns" { # Parameter Name Prefix (for wildcard IAM policies) # ============================================================================= output "parameter_prefix" { - description = "Parameter Store prefix for IAM policies" + description = "Parameter Store prefix (EKS IRSA/IAM wildcard policy에서 사용)" value = "/${var.project}/${var.environment}" } diff --git a/infra/terraform/modules/s3/main.tf b/infra/terraform/modules/s3/main.tf index 402c5cf7..db7747e5 100644 --- a/infra/terraform/modules/s3/main.tf +++ b/infra/terraform/modules/s3/main.tf @@ -206,7 +206,23 @@ resource "aws_s3_bucket_policy" "logs" { "aws:SourceAccount" = var.account_id } } + }, + # ALB Access Logs + { + Sid = "AllowALBAccessLogs" + Effect = "Allow" + Principal = { + Service = "elasticloadbalancing.amazonaws.com" + } + Action = "s3:PutObject" + Resource = "${aws_s3_bucket.logs.arn}/alb-logs/*" + Condition = { + StringEquals = { + "aws:SourceAccount" = var.account_id + } + } } + ] }) } diff --git a/infra/terraform/modules/waf/main.tf b/infra/terraform/modules/waf/main.tf index 5c7f91a0..6edb0165 100644 --- a/infra/terraform/modules/waf/main.tf +++ b/infra/terraform/modules/waf/main.tf @@ -1,5 +1,5 @@ # ============================================================================= -# WAF Web ACL for API Gateway +# WAF Web ALB (EKS Ingress via AWS Load Balancer Controller) # ============================================================================= resource "aws_wafv2_web_acl" "main" { name = "${var.name_prefix}-waf" @@ -111,15 +111,7 @@ resource "aws_wafv2_web_acl" "main" { tags = merge(var.common_tags, { Name = "${var.name_prefix}-waf" }) } -# ============================================================================= -# WAF Association with API Gateway -# ============================================================================= -#resource "aws_wafv2_web_acl_association" "api_gateway" { -# count = var.api_gateway_stage_arn != "" ? 1 : 0 -# -# resource_arn = var.api_gateway_stage_arn -# web_acl_arn = aws_wafv2_web_acl.main.arn -#} + # ============================================================================= # CloudWatch Log Group for WAF diff --git a/infra/terraform/modules/waf/variables.tf b/infra/terraform/modules/waf/variables.tf index 6b92f8a7..4de79d9c 100644 --- a/infra/terraform/modules/waf/variables.tf +++ b/infra/terraform/modules/waf/variables.tf @@ -9,11 +9,6 @@ variable "common_tags" { default = {} } -variable "api_gateway_stage_arn" { - description = "API Gateway Stage ARN" - type = string - default = "" -} variable "rate_limit" { description = "5분당 최대 요청 수 (Rate Limiting)" From f38e2f56b9ae5057411b5c76ef02515dda720706 Mon Sep 17 00:00:00 2001 From: dbswjd7 Date: Fri, 13 Feb 2026 10:07:43 +0900 Subject: [PATCH 08/35] =?UTF-8?q?feat(#322):=20control=20plane=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=A4=80=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/terraform/environments/dev/main.tf | 59 +++++++ infra/terraform/environments/dev/outputs.tf | 30 ++++ infra/terraform/environments/dev/provider.tf | 4 + infra/terraform/environments/dev/variables.tf | 18 +++ infra/terraform/modules/eks-addons/main.tf | 41 +++++ infra/terraform/modules/eks-addons/output.tf | 8 + .../terraform/modules/eks-addons/variables.tf | 14 ++ infra/terraform/modules/eks/main.tf | 151 ++++++++++++++++++ infra/terraform/modules/eks/output.tf | 27 ++++ infra/terraform/modules/eks/variables.tf | 37 +++++ infra/terraform/modules/elasticache/main.tf | 72 ++++----- infra/terraform/modules/irsa/main.tf | 46 ++++++ infra/terraform/modules/irsa/output.tf | 7 + infra/terraform/modules/irsa/variables.tf | 13 ++ 14 files changed, 491 insertions(+), 36 deletions(-) create mode 100644 infra/terraform/modules/eks-addons/main.tf create mode 100644 infra/terraform/modules/eks-addons/output.tf create mode 100644 infra/terraform/modules/eks-addons/variables.tf create mode 100644 infra/terraform/modules/eks/main.tf create mode 100644 infra/terraform/modules/eks/output.tf create mode 100644 infra/terraform/modules/eks/variables.tf create mode 100644 infra/terraform/modules/irsa/main.tf create mode 100644 infra/terraform/modules/irsa/output.tf create mode 100644 infra/terraform/modules/irsa/variables.tf diff --git a/infra/terraform/environments/dev/main.tf b/infra/terraform/environments/dev/main.tf index 1cb06bfa..33eb2a54 100644 --- a/infra/terraform/environments/dev/main.tf +++ b/infra/terraform/environments/dev/main.tf @@ -262,3 +262,62 @@ module "monitoring" { # Redis 모니터링 (선택) redis_cluster_id = "${local.name_prefix}-redis-001" } + +# ============================================================================= +# eks +# ============================================================================= +module "eks" { + source = "../../modules/eks" + + name_prefix = local.name_prefix + common_tags = local.common_tags + + cluster_name = var.cluster_name + cluster_version = var.cluster_version + + vpc_id = module.network.vpc_id + + subnet_ids = module.network.private_subnet_ids + node_subnet_ids = module.network.private_subnet_ids + + endpoint_private_access = true + endpoint_public_access = false + + enable_node_group = false + + enable_node_ssm = true +} + + +data "aws_eks_cluster" "this" { + name = module.eks.cluster_name +} + +module "irsa" { + source = "../../modules/irsa" + + name_prefix = local.name_prefix + common_tags = local.common_tags + + oidc_issuer_url = module.eks.oidc_issuer_url + + service_accounts = { + aws_load_balancer_controller = { + namespace = "kube-system" + service_account = "aws-load-balancer-controller" + policy_json = var.alb_controller_policy_json + } + } +} + +module "eks_addons" { + source = "../../modules/eks-addons" + + common_tags = local.common_tags + cluster_name = module.eks.cluster_name + + enable_vpc_cni = true + enable_coredns = true + enable_kube_proxy = true + enable_ebs_csi = true +} diff --git a/infra/terraform/environments/dev/outputs.tf b/infra/terraform/environments/dev/outputs.tf index 66efc4be..a74ef49e 100644 --- a/infra/terraform/environments/dev/outputs.tf +++ b/infra/terraform/environments/dev/outputs.tf @@ -115,3 +115,33 @@ output "sns_alerts_topic_arn" { description = "알람 알림 SNS Topic ARN" value = module.monitoring.sns_topic_arn } + + +# ============================================================================= +# eks +# ============================================================================= + +output "eks_cluster_name" { + value = module.eks.cluster_name +} + +output "eks_cluster_endpoint" { + value = module.eks.cluster_endpoint +} + +output "eks_cluster_ca" { + value = module.eks.cluster_ca +} + +output "eks_node_security_group_id" { + value = module.eks.node_security_group_id +} + +output "oidc_issuer_url" { + value = try(data.aws_eks_cluster.this.identity[0].oidc[0].issuer, null) +} + + +output "irsa_role_arns" { + value = module.irsa.service_account_role_arns +} diff --git a/infra/terraform/environments/dev/provider.tf b/infra/terraform/environments/dev/provider.tf index a6e68b57..18c133d5 100644 --- a/infra/terraform/environments/dev/provider.tf +++ b/infra/terraform/environments/dev/provider.tf @@ -6,6 +6,10 @@ terraform { source = "hashicorp/aws" version = "~> 5.0" } + tls = { + source = "hashicorp/tls" + version = ">= 4.0" + } # postgresql = { # source = "cyrilgdn/postgresql" # version = "~> 1.21" diff --git a/infra/terraform/environments/dev/variables.tf b/infra/terraform/environments/dev/variables.tf index 09471ef0..e5e752a4 100644 --- a/infra/terraform/environments/dev/variables.tf +++ b/infra/terraform/environments/dev/variables.tf @@ -349,3 +349,21 @@ variable "kafka_log_retention_hours" { type = number default = 168 # 7일 } + + +# ============================================================================= +# eks 설정 +# ============================================================================= +variable "cluster_name" { + cluster_name = "spot-cluster-test" + type = string +} + +variable "cluster_version" { + type = string + default = "1.29" +} + +variable "alb_controller_policy_json" { + type = string +} diff --git a/infra/terraform/modules/eks-addons/main.tf b/infra/terraform/modules/eks-addons/main.tf new file mode 100644 index 00000000..77e5d91d --- /dev/null +++ b/infra/terraform/modules/eks-addons/main.tf @@ -0,0 +1,41 @@ +resource "aws_eks_addon" "vpc_cni" { + count = var.enable_vpc_cni ? 1 : 0 + cluster_name = var.cluster_name + addon_name = "vpc-cni" + addon_version = var.vpc_cni_version != "" ? var.vpc_cni_version : null + + resolve_conflicts_on_update = "OVERWRITE" + tags = var.common_tags +} + +resource "aws_eks_addon" "coredns" { + count = var.enable_coredns ? 1 : 0 + cluster_name = var.cluster_name + addon_name = "coredns" + addon_version = var.coredns_version != "" ? var.coredns_version : null + + resolve_conflicts_on_update = "OVERWRITE" + tags = var.common_tags +} + +resource "aws_eks_addon" "kube_proxy" { + count = var.enable_kube_proxy ? 1 : 0 + cluster_name = var.cluster_name + addon_name = "kube-proxy" + addon_version = var.kube_proxy_version != "" ? var.kube_proxy_version : null + + resolve_conflicts_on_update = "OVERWRITE" + tags = var.common_tags +} + +resource "aws_eks_addon" "ebs_csi" { + count = var.enable_ebs_csi ? 1 : 0 + cluster_name = var.cluster_name + addon_name = "aws-ebs-csi-driver" + addon_version = var.ebs_csi_version != "" ? var.ebs_csi_version : null + + resolve_conflicts_on_update = "OVERWRITE" + service_account_role_arn = var.ebs_csi_irsa_role_arn != "" ? var.ebs_csi_irsa_role_arn : null + + tags = var.common_tags +} diff --git a/infra/terraform/modules/eks-addons/output.tf b/infra/terraform/modules/eks-addons/output.tf new file mode 100644 index 00000000..5100729c --- /dev/null +++ b/infra/terraform/modules/eks-addons/output.tf @@ -0,0 +1,8 @@ +output "enabled_addons" { + value = { + vpc_cni = var.enable_vpc_cni + coredns = var.enable_coredns + kube_proxy = var.enable_kube_proxy + ebs_csi = var.enable_ebs_csi + } +} diff --git a/infra/terraform/modules/eks-addons/variables.tf b/infra/terraform/modules/eks-addons/variables.tf new file mode 100644 index 00000000..8d9004d4 --- /dev/null +++ b/infra/terraform/modules/eks-addons/variables.tf @@ -0,0 +1,14 @@ +variable "common_tags" { type = map(string) default = {} } +variable "cluster_name" { type = string } + +variable "enable_vpc_cni" { type = bool default = true } +variable "enable_coredns" { type = bool default = true } +variable "enable_kube_proxy" { type = bool default = true } +variable "enable_ebs_csi" { type = bool default = true } + +variable "vpc_cni_version" { type = string default = "" } +variable "coredns_version" { type = string default = "" } +variable "kube_proxy_version" { type = string default = "" } +variable "ebs_csi_version" { type = string default = "" } + +variable "ebs_csi_irsa_role_arn" { type = string default = "" } diff --git a/infra/terraform/modules/eks/main.tf b/infra/terraform/modules/eks/main.tf new file mode 100644 index 00000000..8cbe9969 --- /dev/null +++ b/infra/terraform/modules/eks/main.tf @@ -0,0 +1,151 @@ +resource "aws_iam_role" "cluster" { + name = "${var.name_prefix}-eks-cluster-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = "sts:AssumeRole" + Principal = { Service = "eks.amazonaws.com" } + }] + }) + + tags = var.common_tags +} + +resource "aws_iam_role_policy_attachment" "cluster_policy" { + role = aws_iam_role.cluster.name + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" +} + +resource "aws_iam_role_policy_attachment" "vpc_resource_controller" { + role = aws_iam_role.cluster.name + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController" +} + +resource "aws_eks_cluster" "this" { + name = var.cluster_name + role_arn = aws_iam_role.cluster.arn + version = var.cluster_version + + vpc_config { + subnet_ids = var.subnet_ids + endpoint_private_access = var.endpoint_private_access + endpoint_public_access = var.endpoint_public_access + public_access_cidrs = var.public_access_cidrs + } + + enabled_cluster_log_types = var.enabled_cluster_log_types + + tags = merge(var.common_tags, { Name = var.cluster_name }) + + depends_on = [ + aws_iam_role_policy_attachment.cluster_policy, + aws_iam_role_policy_attachment.vpc_resource_controller + ] +} + +resource "aws_security_group" "node" { + name = "${var.name_prefix}-eks-node-sg" + description = "EKS node security group" + vpc_id = var.vpc_id + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = merge(var.common_tags, { + Name = "${var.name_prefix}-eks-node-sg" + }) +} + +resource "aws_iam_role" "node" { + name = "${var.name_prefix}-eks-node-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = "sts:AssumeRole" + Principal = { Service = "ec2.amazonaws.com" } + }] + }) + + tags = var.common_tags +} + +resource "aws_iam_role_policy_attachment" "node_worker" { + role = aws_iam_role.node.name + policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" +} + +resource "aws_iam_role_policy_attachment" "node_cni" { + role = aws_iam_role.node.name + policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" +} + +resource "aws_iam_role_policy_attachment" "node_ecr" { + role = aws_iam_role.node.name + policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" +} + +resource "aws_iam_role_policy_attachment" "node_ssm" { + count = var.enable_node_ssm ? 1 : 0 + role = aws_iam_role.node.name + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" +} + +resource "aws_launch_template" "node" { + name_prefix = "${var.name_prefix}-eks-ng-" + + vpc_security_group_ids = [aws_security_group.node.id] + + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" + http_put_response_hop_limit = 2 + } + + block_device_mappings { + device_name = "/dev/xvda" + ebs { + volume_size = var.node_disk_size + volume_type = "gp3" + encrypted = true + delete_on_termination = true + } + } + + tag_specifications { + resource_type = "instance" + tags = merge(var.common_tags, { + Name = "${var.name_prefix}-eks-node" + }) + } + + tags = var.common_tags +} + +resource "aws_eks_node_group" "default" { + count = var.enable_node_group && var.node_desired_size > 0 ? 1 : 0 + + cluster_name = aws_eks_cluster.this.name + node_group_name = "${var.name_prefix}-node-group" + node_role_arn = aws_iam_role.node.arn + subnet_ids = var.node_subnet_ids + + instance_types = var.node_instance_types + + scaling_config { + desired_size = var.node_desired_size + min_size = var.node_min_size + max_size = var.node_max_size + } + + tags = merge(var.common_tags, { + Name = "${var.name_prefix}-node-group" + }) +} diff --git a/infra/terraform/modules/eks/output.tf b/infra/terraform/modules/eks/output.tf new file mode 100644 index 00000000..a3212482 --- /dev/null +++ b/infra/terraform/modules/eks/output.tf @@ -0,0 +1,27 @@ +output "cluster_name" { + value = aws_eks_cluster.this.name +} + +output "cluster_arn" { + value = aws_eks_cluster.this.arn +} + +output "cluster_endpoint" { + value = aws_eks_cluster.this.endpoint +} + +output "cluster_ca" { + value = aws_eks_cluster.this.certificate_authority[0].data +} + +output "oidc_issuer_url" { + value = aws_eks_cluster.this.identity[0].oidc[0].issuer +} + +output "node_security_group_id" { + value = aws_security_group.node.id +} + +output "node_role_arn" { + value = aws_iam_role.node.arn +} diff --git a/infra/terraform/modules/eks/variables.tf b/infra/terraform/modules/eks/variables.tf new file mode 100644 index 00000000..bab1d3c6 --- /dev/null +++ b/infra/terraform/modules/eks/variables.tf @@ -0,0 +1,37 @@ +variable "name_prefix" { type = string } +variable "common_tags" { type = map(string) default = {} } + +variable "cluster_name" { type = string } +variable "cluster_version" { type = string default = "1.29" } + +variable "vpc_id" { type = string } + +variable "subnet_ids" { type = list(string) } +variable "node_subnet_ids" { type = list(string) } + +variable "endpoint_private_access" { type = bool default = true } +variable "endpoint_public_access" { type = bool default = false } +variable "public_access_cidrs" { type = list(string) default = ["0.0.0.0/0"] } + +variable "enabled_cluster_log_types" { + type = list(string) + default = ["api", "audit", "authenticator", "controllerManager", "scheduler"] +} + +variable "node_instance_types" { type = list(string) default = ["t3.medium"] } +variable "node_capacity_type" { type = string default = "ON_DEMAND" } +variable "node_ami_type" { type = string default = "AL2_x86_64" } + +variable "node_disk_size" { type = number default = 50 } + +variable "node_desired_size" { type = number default = 2 } +variable "node_min_size" { type = number default = 2 } +variable "node_max_size" { type = number default = 4 } + +variable "enable_node_ssm" { type = bool default = true } + +variable "enable_node_group" { + description = "Worker Node Group 생성 여부" + type = bool + default = false +} diff --git a/infra/terraform/modules/elasticache/main.tf b/infra/terraform/modules/elasticache/main.tf index 74425a8b..82b9dbf2 100644 --- a/infra/terraform/modules/elasticache/main.tf +++ b/infra/terraform/modules/elasticache/main.tf @@ -1,42 +1,42 @@ # ============================================================================= # ElastiCache Redis - 캐시 및 세션 저장소 # ============================================================================= - -# ----------------------------------------------------------------------------- -# Subnet Group -# ----------------------------------------------------------------------------- -resource "aws_elasticache_subnet_group" "redis" { - name = "${var.name_prefix}-redis-subnet" - subnet_ids = var.subnet_ids - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-redis-subnet" }) -} - -# ----------------------------------------------------------------------------- -# Security Group -# ----------------------------------------------------------------------------- -resource "aws_security_group" "redis" { - name = "${var.name_prefix}-redis-sg" - description = "Security group for ElastiCache Redis" - vpc_id = var.vpc_id - - ingress { - description = "Redis from EKS nodes" - from_port = 6379 - to_port = 6379 - protocol = "tcp" - security_groups = var.allowed_security_group_ids - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } - - tags = merge(var.common_tags, { Name = "${var.name_prefix}-redis-sg" }) -} +# +# # ----------------------------------------------------------------------------- +# # Subnet Group +# # ----------------------------------------------------------------------------- +# resource "aws_elasticache_subnet_group" "redis" { +# name = "${var.name_prefix}-redis-subnet" +# subnet_ids = var.subnet_ids +# +# tags = merge(var.common_tags, { Name = "${var.name_prefix}-redis-subnet" }) +# } +# +# # ----------------------------------------------------------------------------- +# # Security Group +# # ----------------------------------------------------------------------------- +# resource "aws_security_group" "redis" { +# name = "${var.name_prefix}-redis-sg" +# description = "Security group for ElastiCache Redis" +# vpc_id = var.vpc_id +# +# ingress { +# description = "Redis from EKS nodes" +# from_port = 6379 +# to_port = 6379 +# protocol = "tcp" +# security_groups = var.allowed_security_group_ids +# } +# +# egress { +# from_port = 0 +# to_port = 0 +# protocol = "-1" +# cidr_blocks = ["0.0.0.0/0"] +# } +# +# tags = merge(var.common_tags, { Name = "${var.name_prefix}-redis-sg" }) +# } # ----------------------------------------------------------------------------- # Parameter Group (Redis 설정 커스터마이징) diff --git a/infra/terraform/modules/irsa/main.tf b/infra/terraform/modules/irsa/main.tf new file mode 100644 index 00000000..16ccaf35 --- /dev/null +++ b/infra/terraform/modules/irsa/main.tf @@ -0,0 +1,46 @@ +data "tls_certificate" "oidc" { + url = var.oidc_issuer_url +} + +resource "aws_iam_openid_connect_provider" "this" { + url = var.oidc_issuer_url + client_id_list = ["sts.amazonaws.com"] + thumbprint_list = [data.tls_certificate.oidc.certificates[0].sha1_fingerprint] + + tags = var.common_tags +} + +resource "aws_iam_role" "service_account" { + for_each = var.service_accounts + + name = "${var.name_prefix}-${each.key}-irsa" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = "sts:AssumeRoleWithWebIdentity" + Principal = { Federated = aws_iam_openid_connect_provider.this.arn } + Condition = { + StringEquals = { + "${replace(var.oidc_issuer_url, "https://", "")}:sub" = "system:serviceaccount:${each.value.namespace}:${each.value.service_account}" + "${replace(var.oidc_issuer_url, "https://", "")}:aud" = "sts.amazonaws.com" + } + } + }] + }) + + tags = var.common_tags +} + +resource "aws_iam_role_policy" "inline" { + for_each = { + for k, v in var.service_accounts : k => v + if try(length(v.policy_json), 0) > 0 + } + + name = "${var.name_prefix}-${each.key}-policy" + role = aws_iam_role.service_account[each.key].id + + policy = each.value.policy_json +} diff --git a/infra/terraform/modules/irsa/output.tf b/infra/terraform/modules/irsa/output.tf new file mode 100644 index 00000000..f3a39ca7 --- /dev/null +++ b/infra/terraform/modules/irsa/output.tf @@ -0,0 +1,7 @@ +output "oidc_provider_arn" { + value = aws_iam_openid_connect_provider.this.arn +} + +output "service_account_role_arns" { + value = { for k, r in aws_iam_role.service_account : k => r.arn } +} diff --git a/infra/terraform/modules/irsa/variables.tf b/infra/terraform/modules/irsa/variables.tf new file mode 100644 index 00000000..ebe8c37a --- /dev/null +++ b/infra/terraform/modules/irsa/variables.tf @@ -0,0 +1,13 @@ +variable "name_prefix" { type = string } +variable "common_tags" { type = map(string) default = {} } + +variable "oidc_issuer_url" { type = string } + +variable "service_accounts" { + type = map(object({ + namespace = string + service_account = string + policy_json = string + })) + default = {} +} From 5b031c1bb2c184d4c0c1cf2c4291c999b4c1611f Mon Sep 17 00:00:00 2001 From: first-lounge Date: Sun, 15 Feb 2026 00:48:58 +0900 Subject: [PATCH 09/35] =?UTF-8?q?fix(#332):=20temporal=20=EC=A3=BC?= =?UTF-8?q?=EC=86=8C=20=EB=B0=8F=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=B9=B4=ED=94=84=EC=B9=B4=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infra/eks/aws-ingress.yaml | 48 + infra/eks/kafka/kafka-connect-init.yaml | 35 + infra/eks/kafka/kafka-connect.yaml | 68 + infra/eks/kafka/kafka-ui.yaml | 53 + infra/eks/kafka/kafka.yaml | 98 + .../fluent-bit/fluent-bit-config.yaml | 101 + .../eks/monitoring/fluent-bit/fluent-bit.yaml | 109 + .../monitoring/grafana/dashboards/15661.json | 6708 +++++++++++++++++ .../monitoring/grafana/dashboards/15757.json | 1 + .../monitoring/grafana/dashboards/15760.json | 3025 ++++++++ .../monitoring/grafana/dashboards/9578.json | 3543 +++++++++ .../grafana/dashboards/spot-logs.json | 20 + .../monitoring/grafana/grafana-config.yaml | 48 + infra/eks/monitoring/grafana/grafana.yaml | 132 + infra/eks/monitoring/loki/loki-config.yaml | 47 + infra/eks/monitoring/loki/loki.yaml | 72 + infra/eks/monitoring/monitoring-ingress.yaml | 21 + infra/eks/monitoring/prometheus/value.yaml | 27 + .../spot-gateway-servicemonitor.yaml | 18 + .../spot-order-servicemonitor.yaml | 17 + .../spot-payment-servicemonitor.yaml | 17 + .../spot-store-servicemonitor.yaml | 17 + .../spot-user-servicemonitor.yaml | 17 + infra/eks/spot/postgres.yaml | 69 + infra/eks/spot/redis.yaml | 39 + infra/eks/spot/spot-gateway.yaml | 78 + infra/eks/spot/spot-order.yaml | 73 + infra/eks/spot/spot-payment.yaml | 73 + infra/eks/spot/spot-store.yaml | 73 + infra/eks/spot/spot-user.yaml | 73 + infra/eks/temporal/temporal-ui.yaml | 45 + infra/eks/temporal/temporal.yaml | 67 + infra/k8s/base/kafka/kafka-connect.yaml | 7 + infra/k8s/base/kafka/kafka-ui.yaml | 7 + infra/k8s/base/temporal/temporal-ui.yaml | 9 +- infra/k8s/base/temporal/temporal.yaml | 6 +- infra/spot-chart/templates/deployment.yaml | 68 + infra/spot-chart/templates/ingress.yaml | 23 + 38 files changed, 14948 insertions(+), 4 deletions(-) create mode 100644 infra/eks/aws-ingress.yaml create mode 100644 infra/eks/kafka/kafka-connect-init.yaml create mode 100644 infra/eks/kafka/kafka-connect.yaml create mode 100644 infra/eks/kafka/kafka-ui.yaml create mode 100644 infra/eks/kafka/kafka.yaml create mode 100644 infra/eks/monitoring/fluent-bit/fluent-bit-config.yaml create mode 100644 infra/eks/monitoring/fluent-bit/fluent-bit.yaml create mode 100644 infra/eks/monitoring/grafana/dashboards/15661.json create mode 100644 infra/eks/monitoring/grafana/dashboards/15757.json create mode 100644 infra/eks/monitoring/grafana/dashboards/15760.json create mode 100644 infra/eks/monitoring/grafana/dashboards/9578.json create mode 100644 infra/eks/monitoring/grafana/dashboards/spot-logs.json create mode 100644 infra/eks/monitoring/grafana/grafana-config.yaml create mode 100644 infra/eks/monitoring/grafana/grafana.yaml create mode 100644 infra/eks/monitoring/loki/loki-config.yaml create mode 100644 infra/eks/monitoring/loki/loki.yaml create mode 100644 infra/eks/monitoring/monitoring-ingress.yaml create mode 100644 infra/eks/monitoring/prometheus/value.yaml create mode 100644 infra/eks/monitoring/servicemonitors/spot-gateway-servicemonitor.yaml create mode 100644 infra/eks/monitoring/servicemonitors/spot-order-servicemonitor.yaml create mode 100644 infra/eks/monitoring/servicemonitors/spot-payment-servicemonitor.yaml create mode 100644 infra/eks/monitoring/servicemonitors/spot-store-servicemonitor.yaml create mode 100644 infra/eks/monitoring/servicemonitors/spot-user-servicemonitor.yaml create mode 100644 infra/eks/spot/postgres.yaml create mode 100644 infra/eks/spot/redis.yaml create mode 100644 infra/eks/spot/spot-gateway.yaml create mode 100644 infra/eks/spot/spot-order.yaml create mode 100644 infra/eks/spot/spot-payment.yaml create mode 100644 infra/eks/spot/spot-store.yaml create mode 100644 infra/eks/spot/spot-user.yaml create mode 100644 infra/eks/temporal/temporal-ui.yaml create mode 100644 infra/eks/temporal/temporal.yaml create mode 100644 infra/spot-chart/templates/deployment.yaml create mode 100644 infra/spot-chart/templates/ingress.yaml diff --git a/infra/eks/aws-ingress.yaml b/infra/eks/aws-ingress.yaml new file mode 100644 index 00000000..b9c9a306 --- /dev/null +++ b/infra/eks/aws-ingress.yaml @@ -0,0 +1,48 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: aws-ingress + namespace: spot + annotations: + alb.ingress.kubernetes.io/scheme: internet-facing + alb.ingress.kubernetes.io/target-type: ip # IP 모드 + alb.ingress.kubernetes.io/healthcheck-path: /actuator/health + alb.ingress.kubernetes.io/success-codes: "200" # 헬스 체크 성공 코드 +spec: + ingressClassName: alb + rules: + # spot + - host: spotorder.org + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: spot-gateway + port: + number: 80 +# +# # kafka +# - host: kafka.localhost +# http: +# paths: +# - path: / +# pathType: Prefix +# backend: +# service: +# name: kafka-ui-svc +# port: +# number: 80 +# +# # temporal +# - host: temporal.localhost +# http: +# paths: +# - path: / +# pathType: Prefix +# backend: +# service: +# name: temporal-ui-svc +# port: +# number: 80 \ No newline at end of file diff --git a/infra/eks/kafka/kafka-connect-init.yaml b/infra/eks/kafka/kafka-connect-init.yaml new file mode 100644 index 00000000..d4eb3977 --- /dev/null +++ b/infra/eks/kafka/kafka-connect-init.yaml @@ -0,0 +1,35 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: kafka-connect-init + namespace: spot +spec: + ttlSecondsAfterFinished: 60 # 해당 job 성공 후 60초 뒤에 pod 자동 삭제 + template: + spec: + restartPolicy: OnFailure + containers: + - name: kafka-connect-init + image: curlimages/curl:latest + env: + - name: CONNECT_URL + value: "http://kafka-connect-svc:8083" + envFrom: + - secretRef: + name: spot-secrets + command: ["/bin/sh"] + args: ["/configs/register-connectors.sh"] + volumeMounts: + - name: config-volume + mountPath: /configs + resources: + requests: + memory: "32Mi" + cpu: "10m" + limits: + memory: "64Mi" + cpu: "50m" + volumes: + - name: config-volume + configMap: + name: kafka-connect-init-config \ No newline at end of file diff --git a/infra/eks/kafka/kafka-connect.yaml b/infra/eks/kafka/kafka-connect.yaml new file mode 100644 index 00000000..87f2e608 --- /dev/null +++ b/infra/eks/kafka/kafka-connect.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka-connect + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka-connect + template: + metadata: + labels: + app: kafka-connect + spec: + containers: + - name: kafka-connect + image: quay.io/debezium/connect:3.4.0.Final + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8083 + env: + - name: CONNECT_CONFIG_PROVIDERS + value: 'env' + - name: CONNECT_CONFIG_PROVIDERS_ENV_CLASS + value: 'org.apache.kafka.common.config.provider.EnvVarConfigProvider' + - name: CONNECT_BOOTSTRAP_SERVERS + value: kafka:9092 + - name: GROUP_ID + value: "1" + - name: CONFIG_STORAGE_TOPIC + value: my_connect_configs + - name: OFFSET_STORAGE_TOPIC + value: my_connect_offsets + - name: STATUS_STORAGE_TOPIC + value: my_connect_statuses + - name: KEY_CONVERTER + value: org.apache.kafka.connect.json.JsonConverter + - name: VALUE_CONVERTER + value: org.apache.kafka.connect.json.JsonConverter + - name: CONNECT_KEY_CONVERTER_SCHEMAS_ENABLE + value: "false" + - name: CONNECT_VALUE_CONVERTER_SCHEMAS_ENABLE + value: "false" + - name: LOGGING_LEVEL + value: 'WARN' + - name: CONNECT_LOG4J_LOGGERS + value: "org.apache.kafka.connect.runtime.rest=WARN,org.reflections=ERROR" + resources: + requests: + cpu: "250m" + memory: "512Mi" + limits: + cpu: "750m" + memory: "1Gi" +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka-connect-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: kafka-connect + ports: + - port: 8083 + targetPort: 8083 \ No newline at end of file diff --git a/infra/eks/kafka/kafka-ui.yaml b/infra/eks/kafka/kafka-ui.yaml new file mode 100644 index 00000000..4ec8239a --- /dev/null +++ b/infra/eks/kafka/kafka-ui.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka-ui + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka-ui + template: + metadata: + labels: + app: kafka-ui + spec: + containers: + - name: kafka-ui + image: provectuslabs/kafka-ui:latest + ports: + - containerPort: 8080 + env: + - name: KAFKA_CLUSTERS_0_NAME + value: local-spot-cluster + - name: KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS + value: 'kafka:9092' + - name: KAFKA_CLUSTERS_0_KAFKACONNECT_0_NAME + value: connect + - name: KAFKA_CLUSTERS_0_KAFKACONNECT_0_ADDRESS + value: 'http://kafka-connect-svc:8083' + - name: LOGGING_LEVEL_ROOT + value: WARN + - name: LOGGING_LEVEL_COM_PROVECTUS + value: WARN + resources: + requests: + cpu: "250m" + memory: "256Mi" + limits: + cpu: "500m" + memory: "512Mi" +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka-ui-svc + namespace: spot +spec: + type: ClusterIP + selector: + app: kafka-ui + ports: + - port: 80 + targetPort: 8080 \ No newline at end of file diff --git a/infra/eks/kafka/kafka.yaml b/infra/eks/kafka/kafka.yaml new file mode 100644 index 00000000..800b81ae --- /dev/null +++ b/infra/eks/kafka/kafka.yaml @@ -0,0 +1,98 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: kafka-pvc + namespace: spot +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kafka + namespace: spot +spec: + replicas: 1 + selector: + matchLabels: + app: kafka + template: + metadata: + labels: + app: kafka + spec: + enableServiceLinks: false + containers: + - name: kafka + image: apache/kafka:4.0.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 9092 + - containerPort: 29092 + env: + - name: KAFKA_NODE_ID + value: "1" + - name: KAFKA_PROCESS_ROLES + value: "broker,controller" + - name: KAFKA_CONTROLLER_QUORUM_VOTERS + value: "1@localhost:19093" + - name: KAFKA_LISTENERS + value: "INTERNAL://0.0.0.0:29092,EXTERNAL://0.0.0.0:9092,CONTROLLER://0.0.0.0:19093" + - name: KAFKA_ADVERTISED_LISTENERS + value: "INTERNAL://kafka:29092,EXTERNAL://kafka:9092" + - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP + value: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT" + - name: KAFKA_INTER_BROKER_LISTENER_NAME + value: "INTERNAL" + - name: KAFKA_CONTROLLER_LISTENER_NAMES + value: "CONTROLLER" + - name: CLUSTER_ID + value: "J9Xz7kQPRYyK8VkqH3mW5A" + - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR + value: "1" + - name: KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR + value: "1" + - name: KAFKA_TRANSACTION_STATE_LOG_MIN_ISR + value: "1" + - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE + value: "true" + - name: KAFKA_NUM_PARTITIONS + value: "3" + - name: KAFKA_HEAP_OPTS + value: "-Xmx512M -Xms512M" + - name: KAFKA_LOG4J_ROOT_LOGLEVEL + value: "WARN" + volumeMounts: + - name: kafka-storage + mountPath: /var/lib/kafka/data + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "1Gi" + cpu: "500m" + volumes: + - name: kafka-storage + persistentVolumeClaim: + claimName: kafka-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: kafka + namespace: spot +spec: + selector: + app: kafka + ports: + - name: external + port: 9092 + targetPort: 9092 + - name: internal + port: 29092 + targetPort: 29092 \ No newline at end of file diff --git a/infra/eks/monitoring/fluent-bit/fluent-bit-config.yaml b/infra/eks/monitoring/fluent-bit/fluent-bit-config.yaml new file mode 100644 index 00000000..697237b1 --- /dev/null +++ b/infra/eks/monitoring/fluent-bit/fluent-bit-config.yaml @@ -0,0 +1,101 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: fluent-bit-config + namespace: monitoring +data: + fluent-bit.conf: |- + [SERVICE] + Flush 1 + Daemon Off + Log_Level info + Parsers_File parsers.conf + + [INPUT] + Name tail + Tag kube.* + Path /var/log/containers/*.log + Parser cri + DB /fluent-bit/tail/flb_kube.db + Mem_Buf_Limit 50MB + Skip_Long_Lines On + Refresh_Interval 10 + + Read_from_Head Off + Ignore_Older 10m + + + [FILTER] + Name kubernetes + Match kube.* + Kube_Tag_Prefix kube.var.log.containers. + Kube_URL https://kubernetes.default.svc:443 + Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token + Merge_Log On + Merge_Log_Key log_processed + Keep_Log Off + Labels On + + [FILTER] + Name parser + Match kube.* + Key_Name log_processed + Parser json + Reserve_Data On + + + + # 사용하지 않는 로그제거 + [FILTER] + Name grep + Match kube.* + Exclude log .*\/actuator\/(health|readiness|liveness).* + + [FILTER] + Name grep + Match kube.* + Exclude $kubernetes['namespace_name'] kube-system + [FILTER] + Name grep + Match kube.* + Exclude $kubernetes['namespace_name'] monitoring + + + + [OUTPUT] + Name loki + Match kube.* + Host loki-service.monitoring.svc.cluster.local + Port 3100 + Uri /loki/api/v1/push + + labels job=fluent-bit + label_keys $kubernetes['labels']['app'],$kubernetes['namespace_name'],$kubernetes['container_name'] + line_format json + + workers 1 + Retry_Limit 5 + net.keepalive on + net.keepalive_idle_timeout 10 + + + [OUTPUT] + Name stdout + Match kube.* + Format json_lines + + + parsers.conf: |- + [PARSER] + Name cri + Format regex + Regex ^(?