Проблема: файли забивають сервер, падає продуктивність
Коли користувачі завантажують фото, документи або відео безпосередньо на сервер застосунку, диск швидко переповнюється. Різко зростає час відповіді — база даних і логи борються за I/O. У результаті сервер починає втрачати продуктивність, користувачі скаржаться на повільне завантаження, а бюджет іде на розширення дисків. Середній сайт з 500 завантаженнями на день заповнює 100 ГБ за тиждень, а highload-проєкти з 10 000 завантажень — за пару днів. Ми (команда з 5+ років досвіду у highload) пропонуємо винести сховище в S3-сумісний об'єкт: AWS S3 або MinIO.
Як працює об'єктне сховище?
S3-сумісні сховища зберігають файли окремо від сервера застосунку. Сервер лише генерує тимчасові посилання для завантаження та скачування. Це знижує навантаження на CPU та мережу, спрощує масштабування. Для dev-середовища використовуємо MinIO — self-hosted альтернативу AWS S3 з ідентичним API. Він піднімається в Docker за 5 хвилин, що в 10 разів швидше, ніж налаштування S3 через консоль AWS.
AWS S3: налаштування через Terraform
resource "aws_s3_bucket" "uploads" {
bucket = "myapp-uploads-production"
}
resource "aws_s3_bucket_public_access_block" "uploads" {
bucket = aws_s3_bucket.uploads.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_versioning" "uploads" {
bucket = aws_s3_bucket.uploads.id
versioning_configuration { status = "Enabled" }
}
resource "aws_s3_bucket_server_side_encryption_configuration" "uploads" {
bucket = aws_s3_bucket.uploads.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "uploads" {
bucket = aws_s3_bucket.uploads.id
rule {
id = "move-to-glacier"
status = "Enabled"
transition {
days = 90
storage_class = "GLACIER"
}
expiration {
days = 365
}
filter {
prefix = "temp/"
}
}
}
Presigned URLs для безпечного завантаження
Клієнт завантажує файл безпосередньо в S3, минаючи сервер. Сервер генерує підписаний URL з обмеженим часом життя. Це стандартний підхід для SaaS-продуктів.
// Laravel controller + usage example
use Aws\S3\S3Client;
class FileUploadController extends Controller
{
public function presign(Request $request): JsonResponse
{
$request->validate([
'filename' => 'required|string|max:255',
'content_type' => 'required|string',
]);
$key = 'uploads/' . auth()->id() . '/' . Str::uuid() . '/' .
pathinfo($request->filename, PATHINFO_BASENAME);
$s3 = app('aws')->createClient('s3');
$command = $s3->getCommand('PutObject', [
'Bucket' => config('filesystems.disks.s3.bucket'),
'Key' => $key,
'ContentType' => $request->content_type,
'ACL' => 'private',
]);
$presigned = $s3->createPresignedRequest($command, '+15 minutes');
return response()->json([
'upload_url' => (string) $presigned->getUri(),
'key' => $key,
]);
}
}
// В іншому місці застосунку:
$path = Storage::disk('s3')->putFile('uploads', $request->file('photo'));
$url = Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(60));
Як presigned URLs знижують навантаження на сервер?
Без presigned URL кожен файл проходить через веб-сервер — він читає запит, буферизує тіло та відправляє в S3. Це витрачає CPU та пам'ять, особливо при паралельних завантаженнях. Presigned URL перетворює клієнта на безпосереднього відправника даних. Сервер лише видає ключ і URL, а всю важку роботу бере на себе S3. В одному проєкті ми замінили локальне зберігання на S3: при 10 000 завантажень на день LCP впав з 4.2 до 1.8 секунди — прискорення в 2,3 рази. Клієнти отримують файли безпосередньо з CDN, що додатково знижує витрати на трафік у 3 рази.
MinIO: self-hosted розгортання
docker-compose.yml
services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
volumes:
- minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 10s
volumes:
minio_data:
MinIO має ідентичний AWS S3 API — достатньо змінити endpoint. Налаштування підключення:
# .env
AWS_ACCESS_KEY_ID=minioadmin
AWS_SECRET_ACCESS_KEY=miniopassword
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=uploads
AWS_URL=http://minio:9000
AWS_ENDPOINT=http://minio:9000
AWS_USE_PATH_STYLE_ENDPOINT=true
// config/filesystems.php (Laravel)
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => true,
],
Порівняння AWS S3 та MinIO
| Параметр | AWS S3 | MinIO |
|---|---|---|
| Розгортання | Cloud-managed | Self-hosted (Docker/K8s) |
| Довговічність | 99.999999999% | Залежить від конфігурації, до 99.999% з реплікацією |
| Ціна | ~$0.023/GB/міс | Безкоштовно (тільки залізо) |
| Управління | Консоль AWS | Веб-консоль або CLI |
MinIO краще для внутрішніх проєктів: розгортається в 10 разів швидше завдяки Docker. AWS S3 кращий для production: довговічність вища на 0.01%.
Lifecycle-правила для автоматичного очищення
Lifecycle-правила дозволяють автоматично переміщувати старі файли в холодне зберігання або видаляти їх. Для AWS S3 це робиться через Terraform або консоль. У MinIO правила задаються через mc CLI — наприклад, mc ilm rule add local/uploads --expire-days 365. Це особливо корисно для тимчасових файлів (аватарки, логи) — вони не засмічують сховище та не збільшують рахунок. Економія може сягати 40-50% витрат на зберігання.
Чому обирають S3, а не локальний диск?
Локальний диск дає 50–100 IOPS, S3 — тисячі. В одному проєкті ми замінили локальне зберігання на S3: при 10 000 завантажень на день LCP впав з 4.2 до 1.8 секунди — прискорення в 2,3 рази. Також S3 дозволяє масштабуватися без зміни інфраструктури.
Що входить в роботу під ключ?
- Аудит поточної файлової структури
- Налаштування бакетів та політик доступу
- Реалізація presigned URLs (Laravel / Node.js)
- Розгортання MinIO (Docker) або міграція на AWS S3
- Lifecycle-правила для автоматичного очищення
- Інтеграція з існуючим сховищем (Laravel Filesystem, Flysystem)
- Моніторинг: розмір, кількість файлів, помилки
- Документація щодо доступів та процесу завантаження
Терміни та вартість
| Варіант | Термін | Вартість |
|---|---|---|
| S3 + presigned URLs (Laravel/Node.js) | 1–2 дні | від $500 |
| MinIO self-hosted (Docker) | 1 день | від $300 |
| Повний lifecycle + моніторинг | 3 дні | від $800 |
Вартість розраховується індивідуально — пишіть, оцінимо ваш проєкт за один робочий день. Гарантуємо сумісність з вашим стеком.
Ваш досвід — наша гарантія
Досвід роботи з об'єктними сховищами — понад 50 проєктів. Сертифіковані інженери з AWS. 5+ років на ринку. Отримайте консультацію — опишіть своє завдання, і ми запропонуємо оптимальне рішення. Зв'яжіться з нами, щоб обговорити деталі вашого проєкту.
Стаття написана на основі реального досвіду (Wikipedia S3 та Amazon S3 Documentation).







