Dựng Cloud Storage lưu trữ file với MinIO
By Huy Nguyễn
1.8k views
0 likes
0 comments
0/10 rate
MinIO là một dự án mã nguồn mở cung cấp một giải pháp lưu trữ đám mây phi tập trung và mở rộng, được xây dựng dựa trên cơ sở của Amazon S3 (Simple Storage Service).
Với MinIO, bạn có thể tạo ra một hệ thống lưu trữ dữ liệu mạnh mẽ, linh hoạt và có khả năng mở rộng mà không phải trả phí cho các dịch vụ lưu trữ đám mây thương mại.
Dưới đây là một số đặc điểm chính của MinIO:
1. Mã nguồn mở:
MinIO là một dự án mã nguồn mở được phát triển dành riêng cho việc xây dựng hệ thống lưu trữ đám mây.
Điều này có nghĩa là bạn có thể tận dụng mã nguồn mở để tùy chỉnh và triển khai MinIO theo nhu cầu của mình mà không cần phải phụ thuộc vào các giải pháp thương mại.
2. Tương thích với Amazon S3:
MinIO được xây dựng với mục tiêu tương thích hoàn toàn với giao diện lập trình ứng dụng (API) của Amazon S3.
Điều này có nghĩa là bạn có thể sử dụng các công cụ, thư viện và ứng dụng hiện có đã được phát triển cho Amazon S3 mà không cần thay đổi.
3. Độ mở rộng và hiệu suất cao:
MinIO được thiết kế để có khả năng mở rộng tuyến tính và đạt được hiệu suất cao, giúp bạn xử lý một lượng lớn dữ liệu một cách dễ dàng và hiệu quả. Hệ thống MinIO có thể mở rộng từ một máy chủ đơn giản đến hàng trăm máy chủ, giữ cho hiệu suất ổn định và đáng tin cậy.
4. Hỗ trợ cho nhiều loại dữ liệu:
Ngoài việc lưu trữ các tệp tin, MinIO cũng hỗ trợ việc lưu trữ dữ liệu phi cấu trúc như dữ liệu đối tượng và dữ liệu truy cập ngẫu nhiên (NoSQL).
Điều này mở ra nhiều cơ hội cho việc xây dựng các ứng dụng lưu trữ và xử lý dữ liệu đa dạng.
MinIO cung cấp một giải pháp lưu trữ dữ liệu đám mây mạnh mẽ và linh hoạt, phù hợp cho cả các doanh nghiệp lớn và các dự án khởi nghiệp.
Túm cái váy lại thì chúng ta sẽ dụng một Cloud Storage gần giống với Google Cloud, Microsoft Azure Storage để lưu trữ file hoàn toàn Free :D
Và trong bài viết này, chúng ta sẽ tìm hiểu cách triển khai và sử dụng MinIO bằng Docker, cũng như tích hợp nó vào ứng dụng ASP.NET.
Trước khi đi vào việc Setup thì các bạn nên biết các kiến thức sau:
2. Docker
3. Nginx
Đầu tiên, bạn cần cài đặt Docker trên hệ thống của mình. Điều này có thể thực hiện thông qua trang web chính thức của Docker.
Hướng dẫn cài đặt Docker trên Ubuntu hoặc với Windows tại https://docs.docker.com/desktop/install/windows-install/
Tạo file docker-compose.yml
version: "3.8"
x-minio-common: &minio-common
image: quay.io/minio/minio
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
expose:
- "9000"
- "9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 5s
retries: 5
services:
minio1:
<<: *minio-common
hostname: minio1
volumes:
- data1-1:/data1
- data1-2:/data2
minio2:
<<: *minio-common
hostname: minio2
volumes:
- data2-1:/data1
- data2-2:/data2
minio3:
<<: *minio-common
hostname: minio3
volumes:
- data3-1:/data1
- data3-2:/data2
minio4:
<<: *minio-common
hostname: minio4
volumes:
- data4-1:/data1
- data4-2:/data2
nginx:
image: nginx:1.19.2-alpine
hostname: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "9010:9000"
- "9011:9001"
depends_on:
- minio1
- minio2
- minio3
- minio4
volumes:
data1-1:
data1-2:
data2-1:
data2-2:
data3-1:
data3-2:
data4-1:
data4-2:
Tạo file nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# include /etc/nginx/conf.d/*.conf;
upstream minio {
server minio1:9000;
server minio2:9000;
server minio3:9000;
server minio4:9000;
}
upstream console {
ip_hash;
server minio1:9001;
server minio2:9001;
server minio3:9001;
server minio4:9001;
}
server {
listen 9000;
listen [::]:9000;
server_name localhost;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://minio;
}
}
server {
listen 9001;
listen [::]:9001;
server_name localhost;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# To support websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://console;
}
}
}
Trong file mình tạo ra 4 cụm server MinIO sau đó sử dụng Nginx để Reverse Proxy và Load Balancing cho server trong trường hợp quá tải. Chi tiết các bạn được kỹ 2 file trên nhé
Mở terminal và chạy lệnh sau trong thư mục chứa docker-compose.yml
docker-compose up -d
Truy cập vào http://localhost:9011 từ trình duyệt của bạn và sử dụng thông tin đăng nhập trong file docker-compose (minioadmin/minioadmin) để truy cập vào giao diện quản lý MinIO.
Đối với việc sử dụng API chúng ta sử dụng http://localhost:9010 và cần tạo Access Key + Secret Key trong trang giao diện ở trên (Chi tiết bên dưới bài viết)
Truy cập vào http://localhost:9011 các bạn sẽ thấy giao diện VIP Pro của nó
Chúng ta sẽ từ Upload 1 file lên xem sao nhé.
Đầu tiên thì bạn cần tại 1 Bucket nó giống như tạo folder mới đó :D
Điền tên Bucket và bấm Create
Upload thử một vài file và nó cứ phải gọi là ngon lành cành đào
Một vài tùy chọn khác
Tiếp theo đây mình sẽ hướng dẫn các bạn sử dụng MinIO Client SDK với ASP.NET
Trước khi sử dụng API thì các bạn cần tạo ra Access Key và Access Token như sau:
PM> Install-Package Minio
Sử dụng AddMinio để thêm Minio vào ServiceCollection của bạn
var endpoint = "localhost:9010";
var accessKey = "RRAw0wUURAxI23mnvPpE";
var secretKey = "si1qRLfyFvQoBSqeLJGjoUn6RdBJYo3a7SvABuJb";
var secure = false;
builder.Services.AddMinio(configureClient => configureClient
.WithEndpoint(endpoint)
.WithCredentials(accessKey, secretKey)
.WithSSL(secure)
.Build());
builder.Services.AddScoped<IMinIOCloudService, MinIOCloudService>();
Các bạn lưu ý là endpoint ở đây không phải là cổng giao diện của MinIO nhé. Mình đã nói ở trên rồi bạn nào lỡ chưa đọc kỹ thì xem lại bên trên nhé!
Option WithSSL mình sẽ để false trường hợp api các bạn config yêu cầu ssl thì để true
Model
public class MinIOCloudModel
{
public string FolderName { get; set; } = string.Empty;
public string OriginalFileName { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
public long FileSize { get; set; }
public string ContentType { get; set; } = string.Empty;
public string PublicUrl { get; set; } = string.Empty;
}
Interface
public interface IMinIOCloudService
{
Task<List<MinIOCloudModel>> UploadFileAsync(List<IFormFile> files, string bucketName, bool isPublicBucket = false);
Task<Stream> DownloadFileAsync(string bucketName, string objectName);
Task<string> GetShareLinkAsync(string bucketName, string objectName, int expireTime);
}
public class MinIOCloudService : IMinIOCloudService
{
private readonly IMinioClient _minioClient;
private readonly string _endPoint;
public MinIOCloudService(IMinioClient minioClient)
{
_minioClient = minioClient;
_endPoint = _minioClient.Config.Endpoint;
}
public async Task<List<MinIOCloudModel>> UploadFileAsync(List<IFormFile> files, string bucketName, bool isPublicBucket = false)
{
var fileResponse = new List<MinIOCloudModel>();
try
{
if (files.Count <= 0) return fileResponse;
var isExists = await _minioClient.BucketExistsAsync(new BucketExistsArgs().WithBucket(bucketName));
if (!isExists)
{
await _minioClient.MakeBucketAsync(new MakeBucketArgs().WithBucket(bucketName));
if (isPublicBucket)
{
// Define public policy for the bucket
string publicReadPolicy = @"
{
""Version"": ""2012-10-17"",
""Statement"": [
{
""Effect"": ""Allow"",
""Principal"": ""*"",
""Action"": ""s3:GetObject"",
""Resource"": ""arn:aws:s3:::" + bucketName + @"/*""
}
]
}";
var policyArgs = new SetPolicyArgs()
.WithPolicy(publicReadPolicy)
.WithBucket(bucketName);
await _minioClient.SetPolicyAsync(policyArgs);
}
}
foreach (var file in files)
{
using var stream = new MemoryStream();
await file.CopyToAsync(stream);
stream.Position = 0;
var objectName = DateTime.Now.Ticks + "_" + new string(file.FileName.Where(c => !char.IsWhiteSpace(c)).ToArray());
var putObjectArgs = new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithStreamData(stream)
.WithObjectSize(stream.Length)
.WithContentType(file.ContentType);
await _minioClient.PutObjectAsync(putObjectArgs);
var statObject =
await _minioClient.StatObjectAsync(
new StatObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName));
if (!string.IsNullOrEmpty(statObject.ObjectName))
{
fileResponse.Add(new MinIOCloudModel()
{
FolderName = bucketName,
OriginalFileName = file.FileName,
FileName = statObject.ObjectName,
FileSize = statObject.Size,
ContentType = statObject.ContentType,
PublicUrl = isPublicBucket ? $"{_endPoint}/{bucketName}/{statObject.ObjectName}" : string.Empty
});
}
}
return fileResponse;
}
catch (MinioException e)
{
throw new Exception(e.Message);
}
}
public async Task<Stream> DownloadFileAsync(string bucketName, string objectName)
{
try
{
var memoryStream = new MemoryStream();
var arg = new StatObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName);
var statObject = await _minioClient.StatObjectAsync(arg);
if (!string.IsNullOrEmpty(statObject.ObjectName))
{
var getObjectArgs = new GetObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithCallbackStream((stream) => { stream.CopyTo(memoryStream); });
await _minioClient.GetObjectAsync(getObjectArgs);
}
memoryStream.Position = 0;
return memoryStream;
}
catch (MinioException e)
{
throw new Exception(e.Message);
}
}
public async Task<string> GetShareLinkAsync(string bucketName, string objectName, int expireTime)
{
try
{
var args = new PresignedGetObjectArgs()
.WithBucket(bucketName)
.WithObject(objectName)
.WithExpiry(expireTime * 60);
return await _minioClient.PresignedGetObjectAsync(args);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
}
Cảm ơn bạn đã đọc bài viết của mình về việc triển khai MinIO và tích hợp nó vào ứng dụng .NET.
Nếu bạn thấy nội dung này hữu ích, hãy chia sẻ nó với cộng đồng của bạn để giúp họ khám phá và áp dụng MinIO trong dự án của họ.
Đồng thời, nếu bạn có bất kỳ câu hỏi hoặc ý kiến, đừng ngần ngại chia sẻ với chúng tôi.
Cảm ơn bạn và chúc bạn một ngày tốt lành!