【minio】linux集群部署minio,springboot整合教程,解决Response code: 502, Content-Type: null, body:问题

注:集群教程不建议直接上生产!! 可自行先在测试环境先验证

单机部署minio

官方中文文档地址:
https://minio.org.cn/docs/minio/linux/operations/install-deploy-manage/deploy-minio-single-node-single-drive.html

官方提供了三种方式,本文采用的是二进制方式

在这里插入图片描述

在这里插入图片描述

step1: 下载二进制文件

wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio

step2:权限改成可执行

chmod +x minio

step3: 可选 博主没执行这步
(和windows配置环境变量的功能类似)

sudo mv minio /usr/local/bin/

step4: 二进制方式 需要手动创建service文件

(:保证/etc/systemd/system/minio.service 和 /usr/lib/systemd/system/minio.service只有一份
否则会冲突,官方推荐 /usr/lib/systemd/system/minio.service原因大概是其它方式安装默认就在这个路径下。

本文是二进制安装方式 经常看博主文章的同学应该能发现 我都是尽可能选择免安装的方式

此外 博主并没按推荐路径 也是为了加强一下记忆 选择避免节外生枝的同学可以按推荐路径 )

tips:

# 进入目录, 避免节外生枝可以选择 /usr/lib/systemd/system/ 路径
cd /etc/systemd/system
# 查看有哪些服务
ls
#复制个文件(博主复制的syslog.service)命名为minio.service
cp syslog.service minio.service

这样就可以得到一个格式正确的service文件

minio.service样例:

(注意替换Environment的账号密码以及ExecStart的路径,
不同版本可能配置会有差异 博主使用的minio是截止2024-05-22的最新版本)

[Unit]
Description=MinIO
Documentation=https://minio.org.cn/docs/minio/linux/index.html

[Service]
# create access and secret ,replace your self ,创建新的access secret 
Environment="MINIO_ACCESS_KEY=mengqiuyuni"
Environment="MINIO_SECRET_KEY=12345678"
# modify minioadmin account ,这里则是修改默认账号minioadmin的密码
Environment="MINIO_ROOT_USER=minioadmin"
Environment="MINIO_ROOT_PASSWORD=12345678"
# replace yourself minio path and data dir path
#ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
ExecStart=/app/minio/minio server /app/minio/minio_data/
# MinIO RELEASE.2023-05-04T21-44-30Z adds support for Type=notify (https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=)
# This may improve systemctl setups where other services use `After=minio.server`
# Uncomment the line to enable the functionality
# Type=notify
# Let systemd restart this service always (×)  on-failure(√)
Restart=on-failure
RestartSec=15
# Specifies the maximum file descriptor number that can be opened by this process
LimitNOFILE=65536

# Specifies the maximum number of threads this process can create
TasksMax=infinity

# Disable timeout logic and wait until process is stopped
TimeoutStopSec=infinity
SendSIGKILL=no

[Install]
WantedBy=multi-user.target

# Built for ${project.name}-${project.version} (${project.name})

step5: 测试启动是否成功
(假设minio二进制文件在 /app/minio/minio目录 ,data文件夹需要创建 假设在/app/minio/minio_data/ )

/app/minio/minio server /app/minio/minio_data/

( 如果执行了第三步 那命令就为minio server /app/minio/minio_data/)

step6: 启动服务

# 重新加载
systemctl daemon-reload
# 启动服务
systemctl start minio.service

(注:systemctl start minio.service方式启动 才读取minio.service配置,
如果用minio server /minio_data 的方式启动 不会读取minio.service ,则需要我们先export环境变量)

step7: 检测是否启动成功

sudo systemctl status minio.service
journalctl -f -u minio.service

step8: 访问网页 检查是否能正常登录

网页打开 localhost:9000 (假设在本机)
输入minio.service配置的账号密码登录

在这里插入图片描述

集群部署minio

集群部署的核心在于需要挂载磁盘 否则会报错:drive is part of root drive

本文教程为多节点多磁盘的集群方式 : 2节点 4磁盘 即每个节点对应2个磁盘
(单节点并不算真正的集群)

挂载磁盘步骤:

  1. 创建虚拟设备命名minio bs=1M count=1024 表示创建1m*1024 = 1G 这里仅做测试用 自行调整大小
sudo dd if=/dev/zero of=/minio bs=1M count=1024
  1. 格式化
sudo mkfs.ext4 /minio

如果我们的 /app/minio/minio_data目录已经单机部署过
现在想集群部署 那需先清空文件夹 (前提是测试环境 且数据不重要)

否则会报错:unable initialize backend: /app/minio/minio data drive is already being used in another erasure deployment. (Number of drives specified:+ but the number of drivesfound in the 1st drive’s format.json

(当然新建另一个文件夹也行)

清空文件夹步骤:依次执行命令

cd /app/minio/minio_data
ls -a 
rm -rf ./minio.sys/
#一定要先cd到minio_data目录再用下面这个命令!!
rm -rf *.*
  1. 挂载磁盘
    /app/minio/minio_data是数据目录 不懂参考上文单机教程就知道是什么了
sudo mount /minio /app/minio/minio_data

如果挂载输错了 使用 umount卸载 例如 :umount /minio

多磁盘就是指要挂载多个磁盘 我们需要再创建一个和minio_data类似的目录

(否则会报错:read failed insufficient number of drives online )

# 创建目录
mkdir /app/minio/minio_data_sec
# 对minio_data_sec 文件夹 也来一遍挂载操作
sudo dd if=/dev/zero of=/minio_sec bs=1M count=1024
sudo mkfs.ext4 /minio_sec
sudo mount /minio_sec  /app/minio/minio_data_sec

  1. 永久挂载
    mount命令挂载是一次性的,服务器重启磁盘挂载就掉了 所以我们需要永久挂载
    永久挂载有两种方式 一种是挂载设备路径 一种是挂载uuid ,挂载设备路径的方式更简单
    但设备重命令等操作后就会丢失,所以我们本文采用uuid方式

查看设备uuid命令:

blkid

记下这个uuid 编辑/etc/fstab 文件

vim /etc/fstab

在文件末尾添加 例如:

UUID=4c748d5d-6b45-4781-8e2e-894a8de1b5aa /app/minio/minio_data ext4 defaults 0 0
UUID=3c748d5d-6b45-4781-4a3b-463b2ce2a4ce /app/minio/minio_data_sec ext4 defaults 0 0
  1. 使用下面命令来检测是否挂载成功:
lsblk
# 或
df -h

建议使用lsblk ,因为不小心重复挂载了 用df -h体现不出来
在这里插入图片描述

当然 上面命令只是检测有没有临时挂载成功,检测永久挂载是否成功的方式自然是重启服务器了

# 重启
reboot
  1. 挂载成功后,另一台服务器执行一模一样的操作即可
    (创建两个数据文件夹+挂载两次磁盘)

  2. 启动命令:
    (注意:这里启动第一个节点后会一直报错 这是正常的,因为磁盘数量不够 在启动第二个节点后 报错就停止了 )

    需要先修改一下 minio.service文件

    [Unit]
    Description=MinIO
    Documentation=https://minio.org.cn/docs/minio/linux/index.html
    
    # replace your minio file path
    AssertFileIsExecutable=/app/minio/minio
    
    [Service]
    # 集群中MINIO_ACCESS_KEY ,MINIO_SECRET_KEY旧配置已经失效了
    #Environment="MINIO_ACCESS_KEY=yourAccess"
    #Environment="MINIO_SECRET_KEY=12345678"
    Environment="MINIO_ROOT_USER=minioadmin"
    Environment="MINIO_ROOT_PASSWORD=12345678"
    
    # replace yourself minio path and data dir path
    #ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
    
    ExecStart=/app/minio/minio server  http://192.168.0.1/app/minio/minio_data http://192.168.0.1/app/minio/minio_data_sec http://192.168.0.2/app/minio/minio_data http://192.168.0.2/app/minio/minio_data_sec
    
    # This may improve systemctl setups where other services use `After=minio.server`
    # Uncomment the line to enable the functionality
    # Type=notify
    # Let systemd restart this service always (×)  on-failure(√)
    Restart=on-failure
    RestartSec=15
    # Specifies the maximum file descriptor number that can be opened by this process
    LimitNOFILE=65536
    
    # Specifies the maximum number of threads this process can create
    TasksMax=infinity
    
    # Disable timeout logic and wait until process is stopped
    TimeoutStopSec=infinity
    SendSIGKILL=no
    
    [Install]
    WantedBy=multi-user.target
    
    # Built for ${project.name}-${project.version} (${project.name})
    
    

单机部署教程说过
/app/minio/minio server http://… http://…
这样直接启动是没走minio.service的, 会使用默认账号密码(生产严禁)

所以我们通过systemctl start minio.service来启动

启动后一定要使用下列命令检测是否成功

journalctl -xe

如果报错:Assertion failed on job for minio.service.
那则是AssertFileIsExecutable 路径写错了,填写minio文件的全路径
有的时候单词拼写错误,也会有提示 例如:

在这里插入图片描述

  1. 验证
    在http://192.168.0.1:9001/ 和 http://192.168.0.2:9001/ 都能看到这个页面

在这里插入图片描述
servers 2 drives 4即表示集群部署成功

不放心可以上传张图片,两台服务器的data目录都保存了该图片 即为成功

springboot整合

maven依赖

 <!--minio-->
 <dependency>
     <groupId>io.minio</groupId>
     <artifactId>minio</artifactId>
     <version>8.5.10</version>
 </dependency>
 <!-- lombok springboot依赖略 -->

minio bean config:

@Data
@Configuration
@EnableConfigurationProperties({MinIOConfigProperties.class})
@ConditionalOnClass(FileStorageService.class)
public class MinIOConfig {

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    @Bean
    public MinioClient buildMinioClient() {
        return MinioClient
                .builder()
                .endpoint(minIOConfigProperties.getEndpoint())
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .build();
    }
}
@Data
@ConfigurationProperties(prefix = "minio")
public class MinIOConfigProperties implements Serializable {

    private String accessKey;
    private String secretKey;
    private String bucketName;
    private String endpoint;
    private String readPath;
}

mino服务类


@Slf4j
@EnableConfigurationProperties(MinIOConfigProperties.class)
@Component
@Import(MinIOConfig.class)
public class FileStorageService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    private final static String separator = "/";

    /**
     * @param dirPath
     * @param filename yyyy/mm/dd/file.jpg
     * @return
     */
    public String builderFilePath(String dirPath, String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if (!StringUtils.isEmpty(dirPath)) {
            stringBuilder.append(dirPath).append(separator);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String todayStr = sdf.format(new Date());
        stringBuilder.append(todayStr).append(separator);
        stringBuilder.append(filename);
        return stringBuilder.toString();
    }

    /**
     * 创建bucket
     */
    public void makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * bucket 是否存在
     *
     * @param bucketName
     * @return
     */
    public boolean existBucket(String bucketName) {
        boolean exist = false;
        try {
            exist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minIOConfigProperties.getBucketName()).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return exist;
    }

    /**
     * 上传图片文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadImgFile(String prefix, String filename, InputStream inputStream) {

        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("image/jpg")
                    .bucket(minIOConfigProperties.getBucketName()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
            urlPath.append(separator + minIOConfigProperties.getBucketName());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString();
        } catch (Exception ex) {
            log.error("minio put file error.", ex);
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 上传html文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */

    public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("text/html")
                    .bucket(minIOConfigProperties.getBucketName()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
            urlPath.append(separator + minIOConfigProperties.getBucketName());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString();
        } catch (Exception ex) {
            log.error("minio put file error.", ex);
            ex.printStackTrace();
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */

    public void delete(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);
        // 删除Objects
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();
        }
    }


    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return 文件流
     */

    public byte[] downLoadFile(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);
        InputStream inputStream = null;
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucketName()).object(filePath).build());
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();
        }

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buff = new byte[100];
        int rc = 0;
        while (true) {
            try {
                if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
            } catch (IOException e) {
                e.printStackTrace();
            }
            byteArrayOutputStream.write(buff, 0, rc);
        }
        return byteArrayOutputStream.toByteArray();
    }


}

controller调用(测试简化版代码)

@RestController
@RequestMapping("/test/minio")
public class MinioController {

    @Autowired
    private FileStorageService fileStorageService;

    @PostMapping("/upload")
    @ApiOperation("文件上传")
    public String upload(@RequestPart MultipartFile file) {
        String filePath;
        try {
            filePath = fileStorageService.uploadImgFile("test","test",file.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
            return "上传失败";
        }
        return "成功";
    }


}

yml配置:

minio:
  # 换成自己的地址、key、bucketName
  endpoint: http://192.168.207.130:9000
  accessKey: yourAccessKey
  secretKey: yourSecretKey
  bucketName: test

可能出现的bug

一般正常安装及代码调用是不会出现bug的,但是博主在调用api时,发现会报错:
Non-XML response from server. Response code: 502, Content-Type: null, body:

但是在浏览器直接访问操作minio却可以成功

调试源码也不知道问题出在哪里,网上也没找到答案,最后博主给minio java sdk的作者提了issue ,作者答复是叫我检查proxy或者负载均衡配置等, 发现果然是代理问题。
(现在github的issue已经可以搜到博主的提问了)

解决方案:
1.关闭网络代理
2. 检查springboot的Application启动类是否设置的代理,同样需要关闭

//        String proxyHost = "127.0.0.1";
//        //
//        String proxyPort = "7890";
//        // 对http开启代理
//        System.setProperty("http.proxyHost", proxyHost);
//        System.setProperty("http.proxyPort", proxyPort);
//        // 对https也开启代理
//        System.setProperty("https.proxyHost", proxyHost);
//        System.setProperty("https.proxyPort", proxyPort);

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用和,io.minio.errors.InvalidResponseException: Non-XML response from server. Response code: 400, Content-Type: text/xml的错误信息表明在与MinIO服务器通信时出现了非XML响应。这通常是由于服务器返回的响应与预期的XML格式不匹配所致。 要解决这个问题,可以尝试以下几个步骤: 1. 检查你的MinIO配置信息是否正确。确保endpoint、accessKey、secretKey和bucketName等参数的值都正确,并且没有任何语法错误或缺失。 2. 检查与MinIO服务器的连接是否正常。确保能够正常访问MinIO服务器,并且网络连接没有问题。 3. 检查MinIO服务器的版本和兼容性。确保你使用的MinIO版本与你的应用程序或工具的要求相匹配,以避免由于不兼容性引起的错误。 4. 检查MinIO服务器的日志和错误信息。查看MinIO服务器的日志文件,以获取更多关于400响应的详细信息,可能会提供更多指导解决问题的线索。 需要注意的是,根据提供的信息,无法确定具体的错误原因。因此,上述步骤仅作为一般性建议。如果问题仍然存在,请参考MinIO的官方文档或寻求MinIO社区的支持,以获取更具体的帮助和指导。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [io.minio.errors.InvalidResponseException: Non-XML response from server.](https://blog.csdn.net/qq_36856047/article/details/124154748)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [minio-boshrelease:http:bosh.io的MinIO版本](https://download.csdn.net/download/weixin_42123191/15081232)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟秋与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值