官网:https://min.io/
中文:http://www.minio.org.cn
官方文档:https://docs.min.io/docs
中文文档(滞后):http://docs.minio.org.cn/docs
官方demo:https://github.com/minio/minio-java
(1)Minlo是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
(2)MinlO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似NodeJS,Redis或者MySQL.
(3)优点:
1)部署简单:一个single二进制文件即是一切,还可支持各种平台。
2)minio支持海量存储,可按zone扩展(原zone不受任何影响),支持单个对象最大5TB;
兼容Amazon S3接口,充分考虑开发人员的需求和体验;
3)低冗余且磁盘损坏高容忍,标准且最高的数据冗余系数为2(即存储一个1M的数据对象,实际占用磁盘空间为2M)。但在任意n/2块disk损坏的情况下依然可以读出数据(n为一个纠删码集合(Erasure Coding Set)中的disk数量)。并且这种损坏恢复是基于单个对象的,而不是基于整个存储卷的。
4)读写性能优异
(1)Object: 存储到Minio的基本对象,如文件、字节流,Anything…
(2)Bucket: 来存储Object的逻辑空间。每个Bucket之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶文件夹。
(3)Drive: 即存储数据的磁盘,在MinlO启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在Drive里。
(4)Set: 即一组Drive的集合,分布式部署根据集群规模自动划分一个或多个Set,每个Set中的Drive分布在不同位置。一个对象存储在一个Set上。(For example: {1…64} is divided into 4 sets each of size 16.)
1)一个对象存储在一个Set上
2)一个集群划分为多个Set
3)一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
4)一个SET中的Drive尽可能分布在不同的节点上
MinlO使用纠删码机制来保证高可靠性,使用highwayhash来处理数据损坏( Bit Rot Protection)。关于纠删码,简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。
即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。
(1)minio server的standalone模式,即要管理的磁盘都在host本地。该启动模式一般仅用于实验环境、 测试环境的验证和学习使用。在standalone模式下,还可以分为non-erasure code mode(无纠删码模式)和erasure code mode(纠删码模式)。
(2)non-erasure code mode
在此启动模式下,对于每一份对象数据, minio直接在data 下面存储这份数据,不会建立副本,也不会启用纠删码机制。因此,这种模式无论是服务实例还是磁盘都是“单点”,无任何高可用保障,磁盘损坏就表示数据丢失。
(3)erasure code mode
此模式为minio server实例传入多个本地磁盘参数。一旦遇到多于一个磁盘参数,minio server会自动启用erasure code mode.
erasure code对磁盘的个数是有要求的,如不满足要求,实例启动将失败。erasure code启后,要求传给minio server的endpoint(standalone模式下,即本地磁盘上的目录)至少为4个
(1)基于ceentos7
使用以下命令在运行 64 位 Intel/AMD 架构的 Linux 主机上运行独立的 MinIO 服务器。将/data 替换为您希望 MinIO 存储数据的驱动器或目录的路径。
# 下载minio执行文件
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
# 启动,注意这个每次启动的端口号都会改变,可以进行固定,还可以对用户名和密码进行固定
./minio server /mnt/miniodata
(2)指定端口以及用户密码启动:
# 注意,密码要大于等于8位!!
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=12345678
# 更改配置文件默认路径 (可以不进行修改)
./minio server --config-dir /mnt/config /mnt/data
# 指定端口号(默认是随机的)
./minio server --console-address ":50000" /mnt/miniodata
(3)开启服务器防火墙端口号(不开启,本机访问不了该端口)
# 配置防火墙开放9000端口
firewall-cmd --zone=public --add-port=9000/tcp --permanent
# 更新防火墙配置
firewall-cmd --reload
# 查看已经开放的端口号
firewall-cmd --zone=public --list-ports
(4)通过url访问,如图所示
(4)通过URL地址对文件进行访问
例如图片或视频资源等,在上传到minio服务器之后。
可以通过 http://ip:port/桶名/文件名 的形式访问。
例如:http://8.142.121.122:9000/avatars/123456789.jpg
注意:使用URL访问前,一定要把该桶的访问权限改成public.
(1)首先拉取minio/minio
docker pull minio/minio
(2)使用docker启动
docker run -d -p 9000:9000 -p 50000:50000 --name minio \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=12345678" \
-v /mnt/miniodata:/data \
-v /mnt/minioconfig:/root/.minio \
minio/minio server --console-address ":50000" /data
(3)注意:中文文档过期,要以英文文档进行配置
(1)Minio使用纠删码erasure code和校验和checksum来保护数据免受硬件故障和无声数据损坏。即便您丢失一半数量(N/2)的硬盘,您仍然可以恢复数据。
纠删码是一种恢复丢失和损坏数据的数学算法,Minio采用Reed-Solomon code将对象拆分成N/2数据和N/2奇偶校验块。这就意味着如果是8块盘,一个对象会被分成8个数据块、8个奇偶校验块,你可以丢失任意6块盘〈不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的盘中的数据进行恢复。
(2)使用Minio Docker镜像,在八块盘中启动Minio服务
docker run -d -p 9000:9000 -p 50000:50000 --name minioec \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=12345678" \
-v /mnt/miniodataec/data1:/data1 \
-v /mnt/miniodataec/data2:/data2 \
-v /mnt/miniodataec/data3:/data3 \
-v /mnt/miniodataec/data4:/data4 \
-v /mnt/miniodataec/data5:/data5 \
-v /mnt/miniodataec/data6:/data6 \
-v /mnt/miniodataec/data7:/data7 \
-v /mnt/miniodataec/data8:/data8 \
minio/minio server /data{1...8} --console-address ":50000"
(1)分布式Minio可以让你将多块硬盘(甚至在不同的机器上)组成一个对象存储服务。由于硬盘分布在不同的节点上,分布式Minio避免了单点故障。
(2)分布式存储可靠性常用方法:
分布式存储,很关键的点在于数据的可靠性,即保证数据的完整,不丢失,不损坏。只有在可靠性实现的前提下,才有了追求一致性、高可用、高性能的基础。而对于在存储领域,一般对于保证数据可靠性的方法主要有两类, 一类是冗余法,一类是校验法 。
冗余
冗余法最简单直接,即对存储的数据进行副本备份,当数据出现丢失,损坏,即可使用备份内容进行恢复,而副本备份的多少,决定了数据可靠性的高低。这其中会有成本的考量,副本数据越多,数据越可靠,但需要的设备就越多,成本就越高。可靠性是允许丢失其中份数据。当前已有很多分布式系统是采用此种方式实现,如Hadoop的文件系统(3个副本),Redis的集群,MySQL的主备模式等。
校验
校验法即通过校验码的数学计算的方式,对出现丢失、损坏的数据进行校验、还原。注意,这里有两个作用,一个校验,通过对数据进行校验和( checksum )进行计算,可以检查数据是否完整,有无损坏或更改,在数据传输和保存时经常用到,如TCP协议;二是恢复还原,通过对数据结合校验码,通过数学计算,还原丢失或损坏的数据,可以在保证数据可靠的前提下,降低冗余,如单机硬盘存储中的RAID技术,纠删码(Erasure Code)技术等。MinlO采用的就是纠删码技术。
(3)分布式Minio优势
1)数据保护
分布式Minio采用纠删码来防范多个节点宕机和位衰减bit rot 。
分布式Minio至少需要4个硬盘,使用分布式Minio自动引入了纠删码功能。
2)高可用
单机Minio服务存在单点故障,相反,如果是一个有N块硬盘的分布式Minio,只要有N/2硬盘在线,你的数据就是安全的。不过你需要至少有N/2+1个硬盘来创建新的对象。
例如,一个16节点的Minio集群,每个节点16块硬盘,就算8台服务器宕机,这个集群仍然是可读的,不过你需要9台服务器才能写数据。
3)一致性
Minio在分布式和单机模式下,所有读写操作都严格遵守read-after-write一致性模型。
运行分布式Minio
(1)启动一个分布式Minio实例,只需要把硬盘位置做为参数传给minio server命令即可,然后,你需要在所有其它节点运行同样的命令。
1)分布式Minio里所有的节点需要有同样的access秘钥和secret秘钥,这样这些节点才能建立连接。为了实现这个,你需要在执行minio server命令之前,先将access秘钥和secret秘钥export成环境变量。新版本使用MINIO_ROOT_USER和MINIO_ROOT_PASSWORD。
2)分布式Minio使用的磁盘里必须是干净的,里面没有数据。
3)分布式Minio里的节点时间差 不能超过3秒 ,可以使用 NTP 来保证时间一致。
(2)启动分布式Minio实例,8个节点,每个节点1块盘,需要在8个节点上都运行下面命令。
该模块没有深入去写,目前了解即可。
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=12345678
minio server http://192.168.244.131/export1 http://192.168.244.132/export2 \
http://192.168.244.133/export3 http://192.168.244.134/export4 \
http://192.168.244.135/export5 http://192.168.244.136/export6 \
http://192.168.244.137/export7 http://192.168.244.138/export8
MinIO Client(mc)为ls,cat,cp,mirror,diff,find等UNIX命令提供了一种替代方案。它支持文件系统和兼容Amazon S3的云存储服务(AWS Signature v2和v4)。
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --help
# 可以添加到sbin下,以后该命令可以直接使用
mv mc /usr/local/sbin
将所有的配置信息都存储在~/.mc/config.json中
# 查询mc host配置
mc config host ls
# 添加minio服务(重要)
mc config host add 服务名字 http://ip:port username password
mc config host add minio-server http://128.128.7.254:9000 admin 12345678
# 删除host
mc config host remove minio-server
注意: 我在做这一步时,mc始终连不上minio服务器,是因为没有配置用户及密码。通过浏览器的console界面配置一个admin用户及赋予相应权限即可。
ls # 列出文件和文件夹。
mb # 创建一个存储桶或一个文件夹。
cat # 显示文件和对象内容。
pipe # 将一个STDIN重定向到一个对象或者文件或者STDOUT。
share # 生成用于共享的URL。
cp # 拷贝文件和对象。
mirror # 给存储桶和文件夹做镜像。
find # 基于参数查找文件。
diff # 对两个文件夹或者存储桶比较差异。
rm # 删除文件和对象。
events # 管理对象通知。
watch # 监视文件和对象的事件。
policy # 管理访问策略。
config # 管理mc配置文件。
update # 检查软件更新。
version # 输出版本信息。
# 下载文件
mc cp minio-server/test1/7.jpg /mnt/data/
# 删除文件
mc rm minio-server/test1/03.jpg
# 上传文件
mc cp /mnt/data/7.jpg minio-server/test1/
# 创建bucket
mc mb minio-server/bucket01
# 删除bucket
mc rb minio-server/bucket01
# bucket不为空,可以强制删除 (慎用)
mc rb --force minio-server/bucket01
(1)MinIO Client(mc)提供了“ admin”子命令来对您的MinIO部署执行管理任务。
service # 服务重启并停止所有MinIO服务器
update # 更新更新所有MinIO服务器
info # 信息显示MinIO服务器信息
user # 用户管理用户
group # 小组管理小组
policy # MinIO服务器中定义的策略管理策略
config # 配置管理MinIO服务器配置
heal # 修复MinIO服务器上的磁盘,存储桶和对象
profile # 概要文件生成概要文件数据以进行调试
top # 顶部提供MinIO的顶部统计信息
trace # 跟踪显示MinIO服务器的http跟踪
console # 控制台显示MinIO服务器的控制台日志
prometheus # Prometheus管理Prometheus配置
kms # kms执行KMS管理操作
(2)用户管理:(重要)
mc admin user --help
# 新建用户
mc admin user add minio-server huige
mc admin user add minio-server huige02 12345678
# 查看用户信息
mc admin user list minio-server
# 禁用用户
mc admin user disable minio-server huige
# 启用用户
mc admin user enable minio-server huige
# 删除用户
mc admin user remove minio-server huige
(3)策略管理(重要)
有了用户之后,还要有权限才能访问 policy,查看策略的相关命令
mc admin policy --help
# 列出MinIO上所有的固定策略,主要有consoleAdmin、diagnostics、readonly、readwrite、writeonly
mc admin policy list minio-server
# 查看pllicy信息
mc admin policy info minio-server readonly
(4)添加新的策略
编写策略文件:/mnt/minio-user-policy-json/minio-huige.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:GetObject"
],
"Resource":[
"arn:aws:s3:::huige"
]
},{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::huige/*"
]
}
]
}
(5)将minio-huige.json添加到策略数据库
mc admin policy add 服务名 策略名 json文件
# 举例:
mc admin policy add minio-server lwp-admin /mnt/minio-user-policy-json/minio-huige.json
# 可以进行查看,添加成功没
mc admin policy list minio-server
# 设置用户的访问策略
mc admin policy set minio-server huige-admin user=lwp
官方demo:https://github.com/minio/minio-java
官方文档:https://docs.min.io/docs/java-client-api-reference.html
引入依赖:
io.minio
minio
8.3.0
me.tongfei
progressbar
0.5.3
com.squareup.okhttp3
okhttp
4.8.1
具体写代码,进入github的官方demo或者官方文档,找到相应功能的代码即可。
如下是文件上传的代码举例:
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;
import io.minio.errors.MinioException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class FileUploader {
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
try {
// 创建客户端
MinioClient minioClient = MinioClient.builder()
.endpoint("https://192.168.244.131:9000")
.credentials("admin", "12345678")
.build();
// 看桶存在不存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("桶名").build());
if (!found) {
// 不存在则创建一个该桶名的桶
minioClient.makeBucket(MakeBucketArgs.builder().bucket("桶名").build());
}
// 上传
minioClient.uploadObject(
UploadObjectArgs.builder()
.bucket("桶名")
.object("桶内对象名")
.filename("/home/user/Photos/asiaphotos.zip(文件本地磁盘的地址)")
.build());
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
System.out.println("HTTP trace: " + e.httpTrace());
}
}
}
本部分仍为举例,具体其他功能请参考官网文档或者官方demo。
import io.minio.DownloadObjectArgs;
import io.minio.MinioClient;
import io.minio.ServerSideEncryptionCustomerKey;
import io.minio.errors.MinioException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.KeyGenerator;
public class DownloadObject {
/** MinioClient.getObject() example. */
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
try {
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://192.168.244.131:9000")
.credentials("admin", "12345678")
.build();
/* Amazon S3: */
// MinioClient minioClient =
// MinioClient.builder()
// .endpoint("https://s3.amazonaws.com")
// .credentials("YOUR-ACCESSKEY", "YOUR-SECRETACCESSKEY")
// .build();
// (1)不加密的方式下载:
{
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket("my-bucketname")
.object("my-objectname")
.filename("my-filename(到哪里)")
.build());
}
// (2)加密的方式下载:
{
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
ServerSideEncryptionCustomerKey ssec = new ServerSideEncryptionCustomerKey(keyGen.generateKey());
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket("my-bucketname")
.object("my-objectname")
.filename("my-filename")
.ssec(ssec) // Replace with same SSE-C used at the time of upload.
.build());
}
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
}
}
(1)新建MinioConfig类
@Configuration
public class MinioConfig{
// 这个部分可以用属性配置类去做,看下面(1)(2)两个部分代码就懂了
@Autowired
private MinioProperties minioProperties;
@Bean
public MinioClient minioClient(){
MinioClient minioClient = MinioClient.builder()
.endpoint(minioProperties.getEndpoint())
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
return minioClient;
}
}
(2)其中MinioProperties由yml统一管理。
新建一个类MinioProperties
@Data
@Component
@ConfigurationProperties(prefix="minio")
public class MinioProperties{
private String endpoint;
private String accessKey;
private String secretKey;
}
(3)最后在yml配置文件中这么写:
minio:
endpoint: http://192.168.244.131:9000
accessKey: admin
secretKey: 12345678
@RequestMapping("/download/{fileName}")
public void download(HttpServletResponse response, @PathVariable("fileName") String fileName){
InputStream in = null;
try={
// 获取对象信息
StatObjectResponse stat = minioClient.statObject(
StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 文件下载
in = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.build());
IOUtils.copy(in, response.getOutputStream());
}catch (Exception e) {
System.out.println("Error occurred: " + e);
}finally{
if(in!=null){
try{
in.close();
}catch(Exception e){
log.error(e.getMessage());
}
}
}
}
@PostMapping("/upload")
public Res delete(@RequestParam(name="file", required=false) MultipartFile[] file){
if(file==null || file.length == 0){
return Res.error("上传文件不能为空");
}
List orgfileNameList = new ArrayList<>(file.length);
for(MultipartFile multipartFile : file){
String orgfileName = multipartFile.getOriginalFilename();
orgfileNameList.add(orgfileName);
try{
// 文件上传
InputStream in = multipartFile.getInputStream();
minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(
in, multipartFile.getSize(), -1)
.contentType(multipartFile.getContentType())
.build());
in.close();
}catch(Exception e){
log.error(e.getMessage());
return Res.error("上传失败");
}
}
}
@RequestMapping("/delete/{fileName}")
public Res delete(@PathVariable("fileName") String fileName){
try{
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
}catch(Exception e){
log.error(e.getMessage());
return Res.error("删除失败");
}
return Res.ok("删除成功");
}