前面我们做了分布式文件存储系统FastDFS的部署应用,其安装还是比较繁琐的,而且实际生产的应用限制较大,下面,介绍一款开源的文件系统——MinIO,它是一种对象存储解决方案,提供与 Amazon Web Services S3 兼容的 API 并支持所有核心 S3 功能,同时有完善的官网几文档,在实际生产中也有较多应用。
首先,我们从官网下载MinIO的二进制文件,下载地址,然后上传到我们的服务器上,修改权限chmod +x minio
,然后设置用户名和密码,注意,用户名长度至少3位,密码长度至少8位,export MINIO_ACCESS_KEY=minio
,export MINIO_SECRET_KEY=minioadmin
,然后可以通过nohup ./minio server --address :9000 --console-address :9001 /tools/MinIO/minio_server/data > /tools/MinIO/minio_server/data/minio.log &
命令进行后台启动,这里address对应的是服务端口,而console-address对应的是控制台端口,这样,我们就完成了单机的部署,访问下控制台服务器ip:9001
根据提示创建bucket,注意命名需要符合规则
确认当前bucket的权限是读写权限
通过控制台测试上传文件
由于这里账号密码是通过export声明的,因此每次重启服务都需要重新执行,所以我们将其设为开机自启服务,创建启动脚本miniostart.sh,添加如下内容
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minioadmin
/tools/MinIO/minio_server/minio server --address :9000 --console-address :9001 /tools/MinIO/minio_server/data > /tools/MinIO/minio_server/data/minio.log &
修改权限chmod 777 miniostart.sh
,编辑文件vi /etc/rc.d/rc.local
,将本启动脚本的完整路劲添加进去即可。
minio集群部署,官方建议至少4个节点,因为只有n/2个节点存活才能保证读业务,n/2+1个节点存货才能保证写业务,因此我们这里配置了四台机器,同时需要保证每个节点间的时间保持一致,因此先开启时间同步,我们这里采用ntp的方式,安装yum -y install ntp
,设置开机启动systemctl enable ntpd,systemctl start ntpd
,设置时间同步timedatectl set-ntp yes
,然后更新时间ntpdate -u cn.pool.ntp.org
,设置国内的时区ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
,操作完成后我们可以通过data
命令查看时间是否一致。接下来我们开始部署集群,请注意,暂停操作,先看本文档。创建相关的目录mkdir /home/ysgs/tools/minio/{config,data,logs} -p
,然后上传二进制文件或者执行下载命令wget https://dl.min.io/server/minio/release/linux-amd64/minio 执行安装chmod +x minio
,接着我们创建启动脚本,如下所示
#用户名
export MINIO_ACCESS_KEY=minio
#密码
export MINIO_SECRET_KEY=minioadmin
#minio启动路径,设置配置路径,服务端口9000,控制台端口9001,数据存储路径对应四个节点
/home/ysgs/tools/minio/minio server --config-dir /home/ysgs/tools/minio/config --address ":9000" --console-address ':9001' \
http://192.168.136.128/home/ysgs/tools/minio/data \
http://192.168.136.131/home/ysgs/tools/minio/data \
http://192.168.136.132/home/ysgs/tools/minio/data \
http://192.168.136.133/home/ysgs/tools/minio/data > /home/ysgs/tools/minio/logs/minio.log &
然后我们尝试启动,结果发现日志有报错Drive
/mnt/data1is part of root drive, will not be used (*errors.errorString)
,这是因为minio集群需要单独的磁盘挂载,不能使用文件夹代替,因此,需要先挂载磁盘,先关掉虚拟机,进入设置,添加硬盘(从这里开始操作)
启动虚拟机,切换root用户,查看分区信息lsblk
sdb是未挂在的磁盘,新建目录data2,mkdir /data2
,挂载磁盘mount /dev/sdb /data2
,如果出现错误,执行mkfs.ext4 /dev/sdb
格式化磁盘,再重新挂载,然后通过df -h
查看
这只是临时挂载,我们需要设置自动挂载,编辑挂载配置vim /etc/fstab
,在文件末尾追加如下内容/dev/sdb /data2 ext4 defaults 0 0
,重启虚拟机查看挂载,正常,然后我们更改启动脚本,将其数据目录换成我们的单独挂载磁盘,如下
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minioadmin
/home/ysgs/tools/minio/minio server --config-dir /home/ysgs/tools/minio/config --address ":9000" --console-address ':9001' \
http://192.168.136.128/data2/minio/data \
http://192.168.136.131/data2/minio/data \
http://192.168.136.132/data2/minio/data \
http://192.168.136.133/data2/minio/data > /home/ysgs/tools/minio/logs/minio.log &
然后我们逐个启动,启动成功,我们可以尝试访问任意ip:9001,可以发现都能访问成功,我们在任意一台机器上创建bucket之后,其他控制台都能访问到
然后我们测试控制台上传文件,看其他节点能否查看到
至此,集群搭建成功,然后我们可以通过一台nginx代理我们的集群,修改nginx配置文件,在http块中添加如下内容
#服务端节点
upstream minio{
server 192.168.136.128:9000;
#server 192.168.136.131:9000;
server 192.168.136.132:9000;
server 192.168.136.133:9000;
}
#控制台节点,采取ip_hash策略
upstream minio_client{
ip_hash;
server 192.168.136.128:9001;
#server 192.168.136.131:9001;
server 192.168.136.132:9001;
server 192.168.136.133:9001;
}
#监听服务端请求,主要是springboot项目应用
server{
listen 9000;
server_name localhost;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_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;
#nginx代理console后查看数据报错Couldn't establish WebSocket connection.
#需要在nginx增加配置,使nginx代理支持长连接
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://minio;
}
}
#监听控制台请求
server{
listen 9001;
server_name localhost;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_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;
proxy_connect_timeout 300;
#nginx代理console后查看数据报错Couldn't establish WebSocket connection.
#需要在nginx增加配置,使nginx代理支持长连接
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://minio_client;
}
}
配置好之后,重启nginx,访问nginx的ip:9001,访问成功,可以看到节点信息
至此,minio的集群搭建完成。
首先,添加对应的依赖
io.minio
minio
8.5.4
然后,创建minio的相关配置类,我这里并没有做SpringBoot项目启动,因此写的默认值,且没有交给Spring管理,作为参考即可
import io.minio.MinioClient;
public class MinioConfig {
private String ip ="192.168.136.135";
private int port = 9000;
private String accessKey = "minio";
private String secretKey = "minioadmin";
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(ip,port,false)
.credentials(accessKey,secretKey)
.build();
}
}
可以看到,我们的ip信息是nginx的ip,这样,我们可以操控集群操作。接下来,进行API的相关测试,这里先给一个官方的QuickStart文档和API地址,我们可以参靠API文档,来进行操作,例如,我们创建bucket,我们找到对应的API
点击该方法,可以看到详细的调用
下面是一些调用示例:
package com.example.test.minio;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
public class test {
public static MinioClient getMinioClient () {
MinioConfig minioConfig = new MinioConfig();
return minioConfig.minioClient();
}
/**
* 创建bucket
* @param bucketName
* @throws Exception
*/
public static void createBucket(String bucketName) throws Exception{
BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
if(!getMinioClient().bucketExists(bucketExistsArgs)){
MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
getMinioClient().makeBucket(makeBucketArgs);
}
}
public static void main(String[] args) throws Exception {
createBucket("cluster");
}
}
我们创建名为cluster的bucket,然后,我们去nginx代理的控制台查看,如下
/**
* 根据已有输入流上传文件
* @param bucketName
* @param objectName
* @param inputStream
* @param objectSize
* @param contentType
* @return
* @throws Exception
*/
public static ObjectWriteResponse putObject(String bucketName, String objectName, InputStream inputStream,long objectSize,String contentType) throws Exception{
if(StringUtils.isEmpty(bucketName)){
throw new RuntimeException("bucketName为空");
}
//调用创建bucket方法,主要判断是否存在
createBucket(bucketName);
//objectSize已知,partSize设为-1意为自动设置
long partSize = -1;
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream,objectSize,partSize)
.contentType(contentType)
.build();
ObjectWriteResponse response = getMinioClient().putObject(putObjectArgs);
return response;
}
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream("C:\\Users\\zy_ys\\Desktop\\壁纸\\富士山.png");
putObject("cluster","富士山.png",inputStream,137920,"image/png");
}
其中contentType为文件类型,下面是一些常见的文件后缀和contentType对照
#图片
jpg=image/jpeg
gif=image/gif
png=image/png
ico=image/x-icon
jpeg=image/jpeg
wbmp=image/vnd.wap.wbmp
#视频&音频
mp3=audio/mp3
mp4=video/mp4
#文档
doc=application/msword
pdf=application/pdf
ppt=application/x-ppt
txt=text/plain
xls=application/x-xls
.xml=text/xml
#证书文件
p10=application/pkcs10
p12=application/x-pkcs12
下载minio集群的文件,创建下载流下载
/**
* 创建下载流
* @param bucketName
* @param objectName
* @return
* @throws Exception
*/
public static InputStream getObject(String bucketName,String objectName) throws Exception{
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
return getMinioClient().getObject(getObjectArgs);
}
public static void main(String[] args) throws Exception {
InputStream inputStream = getObject("cluster", "富士山.png");
File file = new File("C:\\Users\\zy_ys\\Desktop\\富士山.png");
OutputStream outputStream = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int length;
while((length = inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,length);
}
outputStream.close();
inputStream.close();
}
获取临时访问连接,可设置有效期,过期后自动失效,如下
/**
* 获取对象的临时访问url,有效期1分钟
* @param bucketName
* @param objectName
* @return
* @throws Exception
*/
public static String getObjectURL(String bucketName,String objectName) throws Exception{
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.expiry(1, TimeUnit.MINUTES)
.method(Method.GET)
.build();
return getMinioClient().getPresignedObjectUrl(args);
}
public static void main(String[] args) throws Exception {
System.out.println(getObjectURL("cluster","富士山.png"));
}
/**
* 删除文件
* @param bucketName
* @param objectName
* @throws Exception
*/
public static void removeObject(String bucketName,String objectName) throws Exception{
RemoveObjectArgs args = RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build();
getMinioClient().removeObject(args);
}
public static void main(String[] args) throws Exception {
removeObject("cluster","富士山.png");
System.out.println("文件已删除");
}