Como ya no tenía sentido seguir usando S3 para pruebas, monté storage con SeaweedFS
Introducción
Si repites una y otra vez en local pruebas de cargas de archivos, presigned URL, buckets públicos, caché CDN y thumbnails, seguir conectando un S3 externo solo para testing termina sintiéndose menos natural de lo que parece. No era solo un tema de costos; también me seguía molestando tener que pasar por cuentas y recursos externos cada vez que quería experimentar.
Por eso esta vez cambié el enfoque. Decidí traer on-premise el storage que uso repetidamente en testing y mantener la interfaz que ve la aplicación lo más cercana posible a S3 그대로. Me parecía una estructura mucho mejor poder conectar un storage on-premise compatible con S3 en local y dev, y cambiarlo directamente por AWS S3 o Cloudflare R2 en producción usando la misma interfaz.
Al final levanté un storage compatible con S3, de un solo nodo, con la combinación SeaweedFS + Nginx CDN + TLS + TTL. Y este trabajo también fue un caso en el que la IA automatizó de punta a punta la investigación inicial, el borrador de infraestructura, la escritura de manifests y la organización del procedimiento de despliegue. En este texto todo dato sensible, como keys reales, Secret, rutas internas e IP internas, está enmascarado.
Por qué lo construí yo mismo
AWS S3 en sí no era el problema. Pero para fines de prueba cada vez se sentía más incómodo.
- Necesitaba validar repetidamente el flujo de carga de archivos en local y en desarrollo.
- Tenía que separar buckets públicos y privados, y revisar junto con eso CDN, presigned URL y la capa de thumbnails.
- Había muchos flujos de creación y borrado de archivos temporales en ciclos cortos.
- Experimentar cada vez sobre un storage externo era demasiado pesado para ser una prueba simple.
Lo que yo quería no era una plataforma gigante de storage, sino 테스트와 검증에 충분한 S3 호환성 y 간단한 온프레미스 운영성. Más concretamente, necesitaba fijar el contrato de almacenamiento que ve el desarrollador como S3 호환 인터페이스, y según el entorno cambiar solo la implementación backend.
Por qué elegí SeaweedFS
Al principio revisé primero la opción más conocida, pero a finales de 2025 era difícil tomar MinIO como la opción por defecto. Por los cambios de licencia y por cómo cambió el rumbo de la comunidad, me pareció una apuesta ambigua para sostener a largo plazo.
En cambio, Ceph RGW era una opción mucho más completa, pero demasiado pesada para este objetivo. Yo necesitaba un storage de prueba de nodo único y una operación sencilla para media, no administrar un clúster de storage a gran escala.
Por eso la elección final fue SeaweedFS.
- Tiene licencia Apache 2.0, así que las restricciones comerciales son ligeras.
- Ofrece S3 Gateway, por lo que no hace falta cambiar demasiado el flujo existente del SDK.
- En el rango principal de uso que yo necesitaba, como uploads, downloads, separación por bucket y presigned URL, la interfaz S3 se mantiene casi intacta.
- Con Helm es fácil desplegarlo de inmediato en Kubernetes.
- Encajaba bien con archivos multimedia pequeños, como imágenes, audio y videos cortos.
Más que encontrar un reemplazo total de S3, fue la elección más simple para este objetivo: S3와 거의 같은 계약으로 움직이는 저장소 dentro del alcance que realmente necesitaba.
Así quedó armado en la práctica
No complicamos la arquitectura. Armé una capa compatible con S3 alrededor de SeaweedFS y le puse delante una capa de caché con Nginx. Encima de eso sumé TLS, una capa de thumbnails para buckets públicos y una política TTL para archivos temporales.
La operación, a grandes rasgos, quedó separada así.
- endpoint S3: storage base al que se conecta la aplicación directamente
- endpoint CDN: entrega con caché de recursos públicos
- endpoint de thumbnails: capa de transformación de imágenes solo para buckets públicos
- separación de buckets:
static,publicson públicos;images,videos,bgm,filesson privados - TTL: el prefijo
_tmp/vence automáticamente después de dos semanas
Con esto pude validar de una sola vez los flujos principales que necesitaba para testing. Uploads, acceso público, presigned URL privadas, caché CDN, generación de thumbnails y limpieza de archivos temporales quedaron todos sobre el mismo storage.
En Spring Boot tampoco cambié demasiado la integración existente con S3. En los perfiles local y de prueba solo cambié el endpoint por una dirección on-premise compatible con S3 y ajusté el acceso path-style junto con las reglas de buckets públicos y privados. Es decir, incluso después de crear un storage nuevo, el código de la aplicación pudo conservar su estructura general.
Eso fue lo que más me gustó. En local y dev puedo usar un storage on-premise como SeaweedFS y, aunque en producción lo cambie por AWS S3 o R2, la interfaz a la que se conecta la aplicación puede mantenerse prácticamente igual. Cambiar de producto de storage deja de implicar rehacer la abstracción de almacenamiento de la aplicación.
Dicho de otro modo, el valor principal de esta implementación no fue simplemente levantar un storage, sino 환경마다 저장소 구현체는 달라도, 코드가 바라보는 인터페이스는 S3 호환으로 고정했다. Ese tipo de interfaz funciona muy bien para testing y también da mucha más flexibilidad si más adelante cambias el storage de producción.
Hasta dónde llegó la IA esta vez
Lo interesante de este trabajo no fue solo SeaweedFS en sí, sino hasta dónde podía empujar la IA todo este proceso.
En la práctica, las siguientes tareas quedaron automatizadas de punta a punta por la IA, y la persona solo intervino para cargar datos sensibles y hacer la revisión final.
- Borrador de investigación y comparación de candidatos de storage compatible con S3
- Reducción de opciones después de comparar
MinIO,SeaweedFS,CephyRustFS - Redacción de borradores para Helm values, manifests de caché Nginx y configuración TLS
- Organización de la estructura de buckets, la política TTL, los procedimientos de prueba y el checklist operativo
- Documentación de la guía de despliegue y del troubleshooting
Lo que confirmé otra vez con esta experiencia es que, si primero defines bien los requisitos y los límites de seguridad, la IA puede automatizar bastante incluso trabajo de infraestructura. Sobre todo en tareas repetitivas como esta, donde el flujo es borrador inicial -> generación de archivos de configuración -> documentación del despliegue -> aplicación real, la mejora de eficiencia se siente mucho.
Cierre
Esta implementación no es la historia grandilocuente de haber adoptado una plataforma de storage. Se parece más al registro de haber traído un poco más a mi terreno ese S3 que terminaba usando repetidamente por culpa de las pruebas.
Pero incluso con eso ya valió la pena. Ahora puedo validar con más frecuencia, sin dependencias externas, flujos reales de operación como recursos públicos y privados, CDN, presigned URL, thumbnails y TTL. Más importante todavía, conseguí una estructura en la que puedo elegir on-premise para local y dev, y S3 o R2 para producción, todo bajo la misma interfaz compatible con S3.
Y algo más quedó claro. La IA no solo puede escribir código; también puede automatizar a un nivel bastante alto borradores de infraestructura, configuración y documentación de despliegue. Sigue habiendo trabajo humano, pero al menos ahora está mucho más claro hasta dónde conviene delegarle.
Apéndice
A continuación está el texto completo de documents/workspace/infrastructure/S3_COMPATIBLE_STORAGE_RESEARCH.md.
Investigación de soluciones de object storage compatibles con S3
Fecha: 2025-12-30 Objetivo: seleccionar una solución de infraestructura propia como alternativa a AWS S3
Antecedentes
Estado de MinIO (diciembre de 2025)
MinIO ya no se recomienda. Razones principales:
-
Problema de licencias: Apache 2.0 → AGPL-3.0
- Si se ofrece como servicio de red, existe obligación de publicar el código fuente
- Para uso comercial, la licencia anual empieza en $96,000
-
Paso a modo de mantenimiento (diciembre de 2025)
- Se suspenden nuevas funciones, mejoras y aceptación de PR
- Los parches de seguridad se aplican solo después de evaluación caso por caso
- Se deja de distribuir el binario de la versión comunitaria (solo se entrega el código fuente)
-
Eliminación de la UI de administración
- En la edición comunitaria se quita la consola de administración
- Todas las funciones completas quedan solo en la versión paga
Referencia: InfoQ - MinIO en modo de mantenimiento
Requisitos
| Ítem | Requisito |
|---|---|
| Compatibilidad S3 | Obligatoria: debe poder usar AWS SDK sin cambios |
| Licencia | Se prefiere una licencia sin restricciones para uso comercial (Apache 2.0, MIT, etc.) |
| Objetos almacenados | imágenes, BGM, videos cortos tipo shorts |
| Expansión CDN | Debe poder montar un CDN con caché mediante Nginx reverse proxy |
| Entorno de despliegue | Kubernetes + Helm chart |
| Costos | Reducir costos de AWS con infraestructura propia |
Comparación de soluciones
1. SeaweedFS ⭐ (recomendado)
| Ítem | Contenido |
|---|---|
| Licencia | Apache 2.0 (uso comercial libre) |
| Lenguaje | Go |
| Arquitectura | estructura Master + Volume + Filer |
| Características | seek de disco O(1), basada en la arquitectura Haystack de Facebook |
| Compatibilidad S3 | ofrece S3 Gateway (soporta perfectamente las operaciones básicas de S3) |
| Helm | chart oficial de Helm + Kubernetes Operator |
| Madurez | validado en producción (casos de despliegue de 1.5PB+) |
| Enterprise | gratis hasta 25TB; luego $1/TB/mes |
Ventajas
- Puede manejar decenas de miles de millones de archivos (ideal para almacenar media)
- Acceso rápido a archivos pequeños gracias al seek O(1)
- Soporta Erasure Coding para mejorar la eficiencia de storage
- Cloud tiering (migración automática de datos fríos)
- Soporte para montaje FUSE, WebDAV e integración con Hadoop
Desventajas
- No soporta algunas funciones avanzadas de S3 (por ejemplo, políticas de lifecycle)
- Respaldo de metadatos obligatorio (si se pierde el metadata del Filer, los archivos quedan huérfanos)
Instalación con Helm
helm repo add seaweedfs https://seaweedfs.github.io/seaweedfs/helm
helm upgrade --install seaweedfs seaweedfs/seaweedfs -n storage --create-namespace
Referencia: SeaweedFS GitHub
2. RustFS
| Ítem | Contenido |
|---|---|
| Licencia | Apache 2.0 |
| Lenguaje | Rust |
| Rendimiento | 2.3 veces más rápido que MinIO con objetos de 4KB |
| Compatibilidad S3 | soporte completo de la API S3 |
| Helm | chart oficial de Helm |
| Madurez | ⚠️ beta (versión 0.0.77 a diciembre de 2025) |
Ventajas
- Soporta migración y convivencia con MinIO
- Muy buen rendimiento con objetos pequeños
- Licencia Apache 2.0
Desventajas
- Sigue en etapa beta (precaución para producción)
- Documentación y comunidad todavía limitadas
Instalación con Helm
helm repo add rustfs https://charts.rustfs.com
helm install rustfs rustfs/rustfs -n rustfs --create-namespace \
--set ingress.className="nginx"
Referencia: RustFS GitHub
3. Ceph RGW (via Rook)
| Ítem | Contenido |
|---|---|
| Licencia | LGPL 2.1 |
| Lenguaje | C++ (ruta de datos), Go (operador Rook) |
| Arquitectura | storage integrado basado en RADOS (Block + File + Object) |
| Compatibilidad S3 | nivel más alto (pasa 576 pruebas de s3-tests) |
| Helm | incluye Rook Operator |
| Madurez | nivel enterprise (validado por años) |
Ventajas
- Primer lugar en compatibilidad S3 (soporta la mayor cantidad de API S3)
- Integra storage de bloques, archivos y objetos
- Multitenancy y aislamiento por namespace
- Configuración avanzada de Erasure Coding
- Escala hasta nivel exabyte
Desventajas
- Instalación y operación complejas
- Altos requisitos de recursos (mínimo 3 nodos y red rápida)
- Curva de aprendizaje pronunciada
Instalación con Helm (Rook)
helm repo add rook-release https://charts.rook.io/release
helm install rook-ceph rook-release/rook-ceph -n rook-ceph --create-namespace
# CephCluster CRD 적용 필요
Referencia: Documentación de Rook
4. Garage
| Ítem | Contenido |
|---|---|
| Licencia | ⚠️ AGPL-3.0 (mismo problema que MinIO) |
| Lenguaje | Rust |
| Características | liviano, especializado en despliegue geográficamente distribuido |
| Compatibilidad S3 | soporta operaciones S3 esenciales (funciones avanzadas limitadas) |
| Helm | chart de la comunidad |
| Madurez | adecuado para self-hosting a pequeña escala |
Ventajas
- Ligero y eficiente en uso de recursos
- Soporte nativo para replicación multi-zona y multi-sitio
- Seguridad de memoria gracias a Rust
Desventajas
- Licencia AGPL-3.0 (restricciones para uso comercial)
- No soporta Erasure Coding (solo replicación 3x)
- Le faltan funciones S3 avanzadas
Referencia: Garage
Comparación de compatibilidad S3
| Solución | Pruebas s3-tests aprobadas | Evaluación |
|---|---|---|
| Ceph RGW | 576 | la mejor |
| Zenko CloudServer | 382 | muy buena |
| MinIO | 321 | buena |
| SeaweedFS | 56 | básica |
SeaweedFS soporta perfectamente las operaciones S3 esenciales (PUT, GET, DELETE, LIST, etc.), pero las funciones avanzadas (Object Lock, Lifecycle, etc.) son limitadas
Recomendación final
🥇 SeaweedFS (recomendación fuerte)
Razones para recomendarlo:
- Licencia Apache 2.0: total libertad para uso comercial
- Optimizado para almacenamiento de media: sobresale con imágenes, audio y video
- Validado en producción: casos de despliegue a gran escala durante años
- Amigable con Kubernetes: Helm chart y Operator oficiales
- Precio razonable: gratis hasta 25TB; luego $1/TB por mes
🥈 Segunda opción
| Situación | Recomendación |
|---|---|
| Si necesitas compatibilidad S3 total | Ceph RGW (aceptando la complejidad) |
| Si prefieres tecnología nueva y experimental | RustFS (aceptando beta) |
| Si el entorno es pequeño y AGPL no importa | Garage |
Guía para armar un CDN con Nginx
Ejemplo de CDN con la combinación SeaweedFS + Nginx:
Configuración de caché en Nginx
# /etc/nginx/nginx.conf
http {
# 캐시 저장소 설정
proxy_cache_path /var/cache/nginx/s3
levels=1:2
keys_zone=s3_cache:100m
max_size=50g
inactive=7d
use_temp_path=off;
upstream seaweedfs_s3 {
server seaweedfs-s3.storage.svc.cluster.local:8333;
keepalive 64;
}
server {
listen 80;
server_name cdn.example.com;
# 이미지 캐싱 (7일)
location ~* \.(jpg|jpeg|png|gif|webp|ico|svg)$ {
proxy_pass http://seaweedfs_s3;
proxy_cache s3_cache;
proxy_cache_valid 200 7d;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
add_header Cache-Control "public, max-age=604800";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 오디오/비디오 캐싱 (30일)
location ~* \.(mp3|wav|ogg|mp4|webm|m4a)$ {
proxy_pass http://seaweedfs_s3;
proxy_cache s3_cache;
proxy_cache_valid 200 30d;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
add_header Cache-Control "public, max-age=2592000";
# Range 요청 지원 (비디오 스트리밍)
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 기타 파일
location / {
proxy_pass http://seaweedfs_s3;
proxy_cache s3_cache;
proxy_cache_valid 200 1d;
add_header X-Cache-Status $upstream_cache_status;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Encabezados de estado de caché
HIT: servido desde cachéMISS: traído desde origenSTALE: se entrega caché vencida (si el origen falla)UPDATING: actualización en segundo plano en curso
Referencia: Guía de caché de NGINX
Arquitectura de despliegue en Kubernetes
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Ingress (Nginx) │ │
│ │ cdn.example.com │ │
│ └──────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────────┐ │
│ │ Nginx Cache Layer (CDN) │ │
│ │ /var/cache/nginx (PVC: 50Gi+) │ │
│ └──────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────────┐ │
│ │ SeaweedFS │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Master │ │ Volume │ │ Volume │ │ Volume │ │ │
│ │ │ (x3) │ │ #1 │ │ #2 │ │ #3 │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌────▼────────────▼────────────▼────────────▼────┐ │ │
│ │ │ Filer (S3 Gateway) │ │ │
│ │ │ seaweedfs-s3:8333 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Persistent Volumes │ │
│ │ Volume #1 Volume #2 Volume #3 │ │
│ │ (100Gi+) (100Gi+) (100Gi+) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Siguientes pasos
- Levantar un entorno de prueba con el chart Helm de SeaweedFS
- Probar la integración con el SDK S3 (verificar compatibilidad con el código actual)
- Configurar la capa de caché Nginx
- Ejecutar benchmarks de rendimiento (imagen, audio, video)
- Definir el plan de despliegue a producción