一姆另、簡介
MinIO 是一個基于Apache License v2.0開源協(xié)議的對象存儲服務(wù)。它兼容亞馬遜S3云存儲服務(wù)接口坟乾,非常適合于存儲大容量非結(jié)構(gòu)化的數(shù)據(jù)迹辐,例如圖片、視頻甚侣、日志文件明吩、備份數(shù)據(jù)和容器/虛擬機(jī)鏡像等,而一個對象文件可以是任意大小殷费,從幾kb到最大5T不等印荔。
二、環(huán)境
ip | 端口 |
---|---|
100 | 9000 |
102 | 9000 |
我這里服務(wù)器就只用了兩臺详羡,會用來做 MinIO的分布式文件仍律。
三、單機(jī)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 賦予執(zhí)行權(quán)限
chmod +x minio
# 啟動 并設(shè)置數(shù)據(jù)存儲位置(單機(jī))
./minio server /data
四实柠、分布式
建議編寫腳本如下 start.sh
# 端口打開 9000
/sbin/iptables -I INPUT -p tcp --dport 9000 -j ACCEPT
# 重新設(shè)置密鑰
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minio@2020
# 啟動并指定分布式服務(wù)器水泉,這里會有坑 可以看 5 標(biāo)題
./minio server \
http://10.240.30.100/home/MinIO/data{1...4} http://10.240.30.102/home/MinIO/data{1...4}
{1...4} 的意思是會在 /home/MinIO/ 生產(chǎn)4個文件 data1 data2 data3 data3,其中 "..." 必須為3個點(diǎn)窒盐,得以獲得最佳的糾刪碼分布草则。啟動成功后會有一個 VIP做為網(wǎng)關(guān)。我自己使用VIP一旦崩潰登钥,無法轉(zhuǎn)移,具體原因尚不清楚娶靡。
糾刪碼 是一種恢復(fù)丟失和損壞數(shù)據(jù)的數(shù)學(xué)算法牧牢,來保護(hù)數(shù)據(jù)免受硬件故障和無聲數(shù)據(jù)損壞。 即便您丟失一半數(shù)量的硬盤,您仍然可以恢復(fù)數(shù)據(jù)塔鳍。內(nèi)部會根據(jù)你提供的磁盤數(shù)量或節(jié)點(diǎn)數(shù)量伯铣,去分配數(shù)據(jù)和奇偶校驗(yàn)塊,一般是對半開轮纫,確保對硬盤故障提供最佳保護(hù)腔寡。Minio糾刪碼的設(shè)計(jì)目標(biāo)是為了性能和盡可能的使用硬件加速。
五掌唾、分布式出現(xiàn)的問題
我很疑惑他是怎么知道我的 nginx1.18.0放前,我并沒有給 MinIO 配置過相關(guān)nginx信息。
API: SYSTEM()
Time: 17:53:35 CST 12/25/2020
DeploymentID: e660d5ad-c358-4aca-bdb5-f9859348b8c6
Error: <html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
(*errors.errorString)
2: cmd/notification.go:468:cmd.(*NotificationSys).updateBloomFilter.func1()
1: pkg/sync/errgroup/errgroup.go:55:errgroup.(*Group).Go.func1()
改到如下配置即可解決
/sbin/iptables -I INPUT -p tcp --dport 9000 -j ACCEPT
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minio@2020
./minio server \
http://10.240.30.102/home/MinIO/data1 http://10.240.30.100/home/MinIO/data1 \
http://10.240.30.102/home/MinIO/data2 http://10.240.30.100/home/MinIO/data2
之前我設(shè)置的是每個節(jié)點(diǎn)會創(chuàng)建4個目錄糯彬,現(xiàn)在改為2個目錄凭语,并沒有在報(bào)錯。在官網(wǎng)看到如下:
MinIO選擇最大的EC(糾刪碼)集大小撩扒,該大小劃分為驅(qū)動器總數(shù)或給定的節(jié)點(diǎn)總數(shù)-確保保持統(tǒng)一分布似扔,即每個節(jié)點(diǎn)每集合參與相等數(shù)量的驅(qū)動器。
我理解的意思是搓谆,在分布式中可能單個節(jié)點(diǎn)的磁盤數(shù)量根據(jù)分布式的節(jié)點(diǎn)數(shù)量而定炒辉。
六、特性及注意
- 在分布式中泉手,如果有16個節(jié)點(diǎn)黔寇,其中8個節(jié)點(diǎn)蹦了,依然可以查看螃诅,但無法編輯或上傳啡氢,只有在可用節(jié)點(diǎn)為9臺才能正常使用。
- 單獨(dú)給分布式其中的某一個節(jié)點(diǎn)上傳文件术裸,文件會被同步到其他節(jié)點(diǎn)倘是。
- 分布式MinIO所需的最小磁盤(目錄)為4,所以小于4就會報(bào)錯袭艺,4是所有節(jié)點(diǎn)的磁盤(目錄)之和搀崭,不是單個節(jié)點(diǎn)需要設(shè)置4個磁盤(目錄),并且使用分布式Minio自動引入了糾刪碼功能猾编。
- 2臺分布式的時候瘤睹,會分配一個主的做為網(wǎng)關(guān)(VIP),當(dāng)VIP被宕答倡,無法在通過VIP訪問到轰传。
- 分布式中密鑰必須一致。
- 2臺分布式的時候瘪撇,其中一臺掛掉另一臺無法使用任何功能获茬。
- minio 自帶控制臺 http://xx.xxx.xxx.xx:9000/minio/login 老是會忘記
- 新建的bucket上傳文件后是沒辦法查看到的港庄,需要修改bucket讀寫權(quán)限后重新上傳。
七恕曲、整合spring boot
依賴
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.0.3</version>
</dependency>
控制層
package com.giant.cloud.controller;
import com.giant.cloud.service.ImgServer;
import com.giant.security.util.ResponseData;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
/**
* @Author big uncle
* @Date 2020/6/15 10:41
**/
@RestController
@RequestMapping("/img")
@RefreshScope
public class ImgController {
@Resource(name = "MinIOServer")
private ImgServer imgServer;
@PostMapping("/push")
public ResponseData push(@RequestParam("file") MultipartFile file,String path,String bucket){
return imgServer.push(bucket,path,file);
}
@PostMapping("/del")
public ResponseData<Boolean> del() {
return imgServer.del();
}
@PostMapping("/get")
public ResponseData get() {
return imgServer.download();
}
}
服務(wù)層
package com.giant.cloud.service.impl;
import com.giant.cloud.code.UploadFileCode;
import com.giant.cloud.config.FileEnvironmentConfig;
import com.giant.cloud.service.ImgServer;
import com.giant.security.util.ResponseData;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
/**
* @author big uncle
* @date 2021/1/11 17:50
* @module
**/
@Service(value = "MinIOServer")
public class MinIOServerImpl implements ImgServer {
@Resource
private ObjectServerImpl objectServer;
@Resource
private FileEnvironmentConfig fileEnvironmentConfig;
@Override
public ResponseData<String> push(String bucket,String path,MultipartFile file) {
try {
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
String id = UUID.randomUUID().toString().replaceAll("-","");
String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String objName = id + milliSecond + fileSuffix;
String str = objectServer.createObject(bucket,path+objName, file.getInputStream());
return ResponseData.successResponse(fileEnvironmentConfig.getGateway()+bucket+"/"+str);
}catch(Exception e){
e.printStackTrace();
return ResponseData.failureResponse(UploadFileCode.UPLOAD_FILE_CODE_1001);
}
}
@Override
public ResponseData<String> download() {
return null;
}
@Override
public ResponseData<Boolean> del() {
return null;
}
}
ObjectServerImpl
package com.giant.cloud.service.impl;
import cn.hutool.core.util.StrUtil;
import com.giant.cloud.code.BucketCode;
import com.giant.cloud.service.ObjectServer;
import com.giant.security.exp.impl.BasicException;
import io.minio.MinioClient;
import io.minio.ObjectWriteArgs;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.InputStream;
/**
* @author big uncle
* @date 2021/1/11 18:39
* @module
**/
@Service
public class ObjectServerImpl implements ObjectServer {
@Resource
private MinioClient minioClient;
@Resource
private BucketServerImpl bucketServer;
@Override
public String putObject(String bucket, String filePath, InputStream inputStream) throws Exception{
if(StrUtil.isBlank(bucket)){
throw new BasicException(BucketCode.BUCKET_CODE_200);
}
if(!bucketServer.bucketExists(bucket)){
throw new BasicException(BucketCode.BUCKET_CODE_201);
}
minioClient.putObject(
PutObjectArgs.builder().bucket(bucket).object(filePath).stream(
inputStream, -1, ObjectWriteArgs.MIN_MULTIPART_SIZE)
.contentType("image/png")
.build());
return filePath;
}
@Override
public String createObject(String bucket,String path, InputStream inputStream) throws Exception {
if(!bucketServer.bucketExists(bucket)){
bucketServer.createBucket(bucket);
}
return putObject(bucket,path,inputStream);
}
@Override
public void delObject(String bucket,String path,String name) throws Exception {
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucket).object(path+name).build());
}
}