diff --git a/kubernetes-aws.yaml b/kubernetes-aws.yaml index 2f4d727..c74033e 100644 --- a/kubernetes-aws.yaml +++ b/kubernetes-aws.yaml @@ -40,6 +40,25 @@ spec: - name: S3_BUCKET_NAME # [수정 필요] Outpost 내 생성한 S3 버킷 명을 입력하세요. value: "autoflow-outpost-bucket" + volumeMounts: + - name: storage-volume + mountPath: /app/storage + volumes: + - name: storage-volume + persistentVolumeClaim: + claimName: autoflow-storage-pvc +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: autoflow-storage-pvc + namespace: autoflow +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi --- apiVersion: v1 kind: Service diff --git a/src/main/java/kr/re/etri/autoflow/config/StorageStaticConfig.java b/src/main/java/kr/re/etri/autoflow/config/StorageStaticConfig.java new file mode 100644 index 0000000..f29d0af --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/config/StorageStaticConfig.java @@ -0,0 +1,36 @@ +package kr.re.etri.autoflow.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.io.File; + +@Configuration +public class StorageStaticConfig implements WebMvcConfigurer { + + @Value("${storage.local.base-path:/app/storage}") + private String basePath; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // PVC 마운트 경로를 HTTP로 접근 가능하도록 설정 + // 예: /api/storage/mlpipeline/test.txt -> /app/storage/mlpipeline/test.txt 호출 + + String location = basePath; + if (!location.endsWith("/")) { + location += "/"; + } + + // Windows 환경 고려 (C:/... 형식일 경우 file:///C:/...) + if (!location.startsWith("/") && location.contains(":")) { + location = "file:///" + location; + } else { + location = "file:" + location; + } + + registry.addResourceHandler("/api/storage/**") + .addResourceLocations(location); + } +} diff --git a/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java b/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java index d63f289..44e205f 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java @@ -92,7 +92,7 @@ public class PipelineUploadController { response.put("pipeline", result); response.put("workflow", workflow); response.put("attachment", attachment); - response.put("minioUrl", minioUrl); + response.put("storageUrl", minioUrl); return ResponseEntity.ok(response); diff --git a/src/main/java/kr/re/etri/autoflow/controllers/StorageAttachmentController.java b/src/main/java/kr/re/etri/autoflow/controllers/StorageAttachmentController.java index 66576e6..85e811c 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/StorageAttachmentController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/StorageAttachmentController.java @@ -38,7 +38,7 @@ import java.util.*; @RequestMapping("/api/attachments") @RequiredArgsConstructor @Slf4j -@Tag(name = "첨부파일", description = "MinIO 첨부파일 관리 API") +@Tag(name = "첨부파일", description = "첨부파일 관리 API") public class StorageAttachmentController { private final StorageAttachmentService storageAttachmentService; @@ -87,7 +87,7 @@ public class StorageAttachmentController { return ResponseEntity.notFound().build(); } - @Operation(summary = "파일 다운로드", description = "MinIO에서 파일을 다운로드합니다.") + @Operation(summary = "파일 다운로드", description = "스토리지에서 파일을 다운로드합니다.") @GetMapping("/download") public ResponseEntity downloadFile(@RequestParam String objectName) { try { @@ -135,7 +135,7 @@ public class StorageAttachmentController { // } - @Operation(summary = "MinIO YAML 파일 읽기", description = "MinIO에서 YAML 파일을 다운로드하여 텍스트로 반환합니다.") + @Operation(summary = "YAML 파일 읽기", description = "스토리지에서 YAML 파일을 다운로드하여 텍스트로 반환합니다.") @GetMapping(value = "/readYamlText", produces = MediaType.TEXT_PLAIN_VALUE) public ResponseEntity readYamlTextFromMinio(@RequestParam String objectName) { try { @@ -169,7 +169,7 @@ public class StorageAttachmentController { Map response = new HashMap<>(); response.put("attachment", saved); - response.put("minioUrl", storageAttachmentService.getFileUrl(saved.getStoragePath())); + response.put("storageUrl", storageAttachmentService.getFileUrl(saved.getStoragePath())); return ResponseEntity.ok(response); @@ -199,7 +199,7 @@ public class StorageAttachmentController { Map response = new HashMap<>(); response.put("attachment", updated); - response.put("minioUrl", storageAttachmentService.getFileUrl(updated.getStoragePath())); + response.put("storageUrl", storageAttachmentService.getFileUrl(updated.getStoragePath())); return ResponseEntity.ok(response); diff --git a/src/main/java/kr/re/etri/autoflow/service/storage/FileSystemStorageProvider.java b/src/main/java/kr/re/etri/autoflow/service/storage/FileSystemStorageProvider.java index 4bf6a3c..791f3ee 100644 --- a/src/main/java/kr/re/etri/autoflow/service/storage/FileSystemStorageProvider.java +++ b/src/main/java/kr/re/etri/autoflow/service/storage/FileSystemStorageProvider.java @@ -97,9 +97,8 @@ public class FileSystemStorageProvider implements StorageProvider { @Override public String getFileUrl(String bucketName, String objectName, String type) { - // 로컬 파일 경로를 URL로 반환하거나 상대 경로로 반환 - // 프론트엔드에서 접근 가능한 경로여야 할 경우 추가적인 서블릿 설정이 필요할 수 있음 - String targetDir = getTargetDir(bucketName); - return Paths.get(targetDir, objectName).toAbsolutePath().toUri().toString(); + String bucket = (bucketName == null || bucketName.isEmpty()) ? defaultBucket : bucketName; + // /api/storage/bucket/object_name 형식으로 반환하여 프론트엔드에서 편리하게 접근 가능하게 함 + return String.format("/api/storage/%s/%s", bucket, objectName); } } diff --git a/src/main/resources/application-aws.properties b/src/main/resources/application-aws.properties index f1b9b9d..bce0d70 100644 --- a/src/main/resources/application-aws.properties +++ b/src/main/resources/application-aws.properties @@ -38,3 +38,6 @@ springdoc.swagger-ui.disable-swagger-default-url=true # Multipart limit spring.servlet.multipart.max-file-size=500MB spring.servlet.multipart.max-request-size=500MB + +# ALB / Forwarded Headers +server.forward-headers-strategy=native diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties index f9e724e..fb59cf1 100644 --- a/src/main/resources/application-prod.properties +++ b/src/main/resources/application-prod.properties @@ -7,3 +7,6 @@ springdoc.swagger-ui.doc-expansion=none springdoc.swagger-ui.disable-swagger-default-url=true spring.jpa.hibernate.ddl-auto=none spring.sql.init.mode=never + +# ALB / Forwarded Headers +server.forward-headers-strategy=native diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6f51392..98e6c66 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -54,7 +54,7 @@ springdoc.swagger-ui.tags-sorter=alpha storage.provider=minio # Local FileSystem ?? -storage.local.base-path=./storage +storage.local.base-path=/app/storage storage.local.default-bucket=mlpipeline # MinIO ??