全栈开发实战——MinIO

原创
2020/12/08 18:59
阅读数 894

介绍

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

Server服务端安装

  1. 二进制文件安装

(服务器文件可能下载比较慢,我本地下载完后上传到服务器的)

// 创建目录
mkdir /minio
mkdir /minio/data
// 下载
cd /minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio
// 赋予可执行权限
chmod +x minio
// 启动服务
./minio server /minio/data

效果如下(ip是显示的内网的,可以用外网地址打开,如果打不开查看下端口是不没有开放):

默认生成的AccessKey和SecretKey如上图都是是minioadmin,最后一行提示我们应该立即修改账号和密码!!!)

  1. docker 安装

上传文件

进入系统后,我们先要点击右下角的“+”按钮,创建一个文件桶(输入名称后,回车即可),在上传文件到这个文件桶中。Create bucket(创建文件桶)、Upload file(上传文件)。

文件桶可以理解为目录,上传的文件必须要放到某个桶里。查看服务器我们看到相应的文件确实生成了,因为我们现在并不是分布式存储,没有使用纠删码的模式,所以直接是源文件。

操作文件

文件最右边有操作按钮,点击后会弹出分享、预览、下载和删除按钮。

分享会生成一个很长的链接,并可以指定有效期。默认最大是7天,可以更改文件桶(Bucket)的策略(Policy)

后台启动

我们的minio之前是通过命令行直接启动的,并没有指定后台运行,而且端口跟AccessKey和SecretKey都还是使用默认的,ctrl+c 关闭会话网址就访问不了了,所以我们需要在启动的时候指定。

(账号密码也可以修改 /minio/data 里面的 /.minio.sys/config/config.jsoncredential节点,修改 access_key 和 secret_key 后面value中的值就可以了。)

新建一个名为minio_start.sh脚本文件:

#!/usr/bin/env sh

# 设置用户名
export MINIO_ACCESS_KEY=minioadmin
# 设置用户密码
export MINIO_SECRET_KEY=123456
# 后台启动,将启动日志输出到/minio/minio_start.log,以1个本地节点启动minio单节点
nohup /minio/minio server /minio/data > /minio/minio_start.log 2>&1 &

echo 'minio is running'

赋予可执行权限chmod +x minio_start.sh

执行文件 ./minio_start.sh

我发现密码没有运行成功,是因为脚本文件修改并运行了好几次,可以用ps -ef | grep minio 查出进程,然后kill该进程。

关闭服务:

使用ps -aux | grep 'sh脚本文件nohub之后的第一个参数',我这里是ps -aux | grep '/minio/minio',查到相应进程,然后kill掉。

https 安全访问

虽然在官方文档中说明了如何使用HTTPS,但是通过Nginx更灵活,且后期调整也比较方便。这里参考的是官方实战秘籍-为MinIo设置Nginx代理

  1. 将以下内容添加为文件/etc/nginx/sites-enabled,例如/etc/nginx/sites-enables/minio

我们代理的是所有请求,所以location 直接设置为 /,上面实战秘籍有指导单独代理某个存储桶,有需要的自己配置下。

# http 跳转到 https
server {
  listen 80;
  server_name example.com;
  rewrite ^(.*)$ https://$host$1 permanent;
}

# 以下属性中以ssl开头的属性代表与证书配置有关,其他属性请根据自己的需要进行配置。
server {
  listen 443 ssl; #SSL协议访问端口号为443。此处如未添加ssl,可能会造成Nginx无法启动。
  server_name example.com; #将localhost修改为您证书绑定的域名,例如:www.example.com。
  ssl_certificate cert/minio/domain name.pem; #将domain name.pem替换成您证书的文件名。
  ssl_certificate_key cert/minio/domain name.key; #将domain name.key替换成您证书的密钥文件名。
  ssl_session_timeout 5m;
  ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #使用此加密套件。
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #使用该协议进行配置。
  ssl_prefer_server_ciphers on;

  # 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 1000m;
  # To disable buffering
  proxy_buffering off;

  location / {
    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 Host $http_host;

    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://localhost:9000; # If you are using docker-compose this would be the hostname i.e. minio
    # Health Check endpoint might go here. See https://www.nginx.com/resources/wiki/modules/healthcheck/
    # /minio/health/live;
  }
}

注意:

  • 用您自己的主机名替换example.com
  • http://localhost:9000 用您自己的服务器名称替换。
  • 添加client_max_body_size 1000m;为了能够上传大文件,请在http上下文中进行操作-只需相应地调整该值即可。1m对于大多数情况,默认值太低。要禁用客户端请求正文大小的检查,请设置client_max_body_size为0。
  • Nginx默认缓冲响应。要禁止Nginx将MinIO响应缓冲到临时文件,请设置proxy_buffering off;。这将缩短到达客户请求的第一个字节的时间。
  • Nginx默认情况下不允许特殊字符。设置ignore_invalid_headers off;为允许带有特殊字符的标题。
  1. 通过从服务器目录创建 symlink (指针)来激活服务器块。配置文件站点从sites-available目录移至sites-enabled目录,执行命令:ln -s /etc/nginx/sites-available/minio /etc/nginx/sites-enabled/minio
  2. 重新启动Nginx服务,执行命令: systemctl restart nginx

Clinet客户端安装

MinIO Client (mc)为ls,cat,cp,mirror,diff,find等UNIX命令提供了一种替代方案。它支持文件系统和兼容Amazon S3的云存储服务(AWS Signature v2和v4)。

流程基本和安装server端一致

  1. 二进制文件安装
// 下载
cd /minio
wget https://dl.min.io/client/mc/release/linux-amd64/mc
// 赋予可执行权限
chmod +x mc

2.添加一个云存储服务

添加一个或多个S3兼容的服务,mc将所有的配置信息都存储在~/.mc/config.json文件中。别名就是给你的云存储服务起了一个短点的外号。S3 endpoint,access key和secret key是你的云存储服务提供的。API签名是可选参数,默认情况下,它被设置为"S3v4"。

mc config host add <ALIAS> <YOUR-S3-ENDPOINT> <YOUR-ACCESS-KEY> <YOUR-SECRET-KEY> [--api API-SIGNATURE]

示例:

./mc config host add minio192_168_1_1 http://192.168.1.1:9000 minioadmin minioadmin --api s3v4

  1. 验证

列出别名为 minio 的云存储服务上的所有存储桶

mc ls minio

  1. 删除一个主机

可以手动编辑${HOME}/.mc/config.json文,所有的配置信息都在这个文件里,我的完整路径是/root/.mc/config.json

这里我们使用命令 ./mc config host ls 列出所有主机(除了红色标注的其他的都是官方默认添加的,如play是官方演示服务,local是本地的服务,我也已经把账号密码都配置上了),然后使用./mc config host rm minio删除

备份文件到 MinIO

上面是在 mac 电脑上安装了 client 端测试用的,实际上我们需要的是服务器到服务器的文件备份。

真实场景:这里我们以备份 mysql 数据文件为例,我们现在有两台服务器,一台主服务器A,一台服务器B。A服务器部署了我们的MinIo,B服务器放一些小项目,现在我们想把B服务器的 mysql 数据文件持续备份到MinIo上,一方面我们可以直接通过MinIo的web站点下载数据文件,不再需要 FTP 传输了。另一方面,我们对数据做了异地备份,一旦出事我们可以快速的恢复数据,这是很重的。

流程如下:

  1. 在B服务器部署 Client 端

    安装Client端流程参考前面介绍。

添加A服务器主机连接,因为我们前面已经添加了https,所以直接使用域名的形式。主机连接名字不能加.,所以使用了下划线。密码由于包含了特殊字符,所以需要用''引号包裹。

./mc config host add minio_www_example_com  https://www.example.com minioadmin 'minioadmin!@#' --api s3v4
  1. 定时自动备份 mysql

    如何定时自动备份可以参考我的文章《debian9+mysqldump+crontab 定时自动完整备份》

  2. 创建一个存储桶

mb 是创建存储桶的指令,我们使用客户端 mc 的 mb 指令在A主服务器创建一个名为mysqlbkp-www.example.com的存储桶,当然使用web页面创建也一样(好像不创建也是可以的)。

./mc mb minio_www_example_com/mysqlbkp-www.example.com
  1. 连续将本地文件镜像到A服务器
./mc mirror --overwrite --watch /本地目录 主机minio_www_example_com/存储桶mysqlbkp-www.example.com

这个是不能后台运行的,你ctrl+c之后它就不再运行了。如果有需要的话可以用nohup后台启动,就像前面后台启动minio服务一样。

新建一个名为mc_mirror.sh脚本文件:

#!/usr/bin/env sh

# 后台启动 mc mirror,将启动日志输出到/minio/mc_mirror.log
nohup /minio/mc mirror --overwrite --watch /本地目录 minio_www_example_com/mysqlbkp-www.example.com > /minio/mc_mirror.log 2>&1 &

echo 'mc mirror is running'

赋予可执行权限chmod +x mc_mirror.sh

执行文件 ./mc_mirror.sh

  1. 关闭服务

使用ps -aux | grep 'sh脚本文件nohub之后的第一个参数'ps -aux | grep '/minio/mc',查到相应进程,然后kill掉。

最终效果图

编程上传文件

提供一个 egg.js 的实例,一定要仔细看备注,并结合前面写的内容才行。

'use strict';

const Minio = require('minio')

const Controller = require('./base');

class FileObsController extends Controller {

  constructor(ctx) {
    super(ctx);

    // 初始化 minio 客户端
    this.minioClient = new Minio.Client({
      endPoint: 'www.example.com',
      // port: 9000,
      useSSL: true,
      accessKey: 'minioadmin',
      secretKey: '123456'
    });
  }

  /**
   * 上传图片
   * @return {Promise<void>}
   */
  async upload() {
    const { ctx } = this;
    try {
      const accept = 'image'
      const { name } = this.config.myConfig.private
      const currentUser = ctx.request.user
      const bucketName = name

      /* 获取文件流 */
      const stream = await ctx.getFileStream();
      console.log('-----------获取数据 start--------------');
      console.log(stream);
      // formData中的其他字段
      console.log(stream.fields);
      console.log('-----------获取数据 end--------------');

      /* 验证并创建存储桶 */
      const exists = await this.minioClient.bucketExists(bucketName)
      console.log('step 1')
      if (!exists) {
        // 创建项目的存储桶
        await this.minioClient.makeBucket(bucketName)
        // 设置存储桶策略(默认是私有,url 不能直接访问)
        // 向匿名用户授予只读权限(其他策略请查阅官方文档)
        const policy = {
          Version: '2012-10-17',
          Statement: [
            {
              Sid: 'PublicRead',
              Effect: 'Allow',
              Principal: '*',
              Action: ['s3:GetObject', 's3:GetObjectVersion'],
              Resource: [`arn:aws:s3:::${bucketName}/*`]
            }
          ]
        }
        await this.minioClient.setBucketPolicy(bucketName, JSON.stringify(policy))
      }

      /* 上传文件 */
      // 使用旧的文件名,允许被覆盖
      // const objectName = stream.filename
      // 使用新的文件名,防止被覆盖
      const objectName = `${accept}/${Date.now()}_${stream.filename}`
      const size = stream.readableLength
      console.log(size)
      // 设置 metaData
      const metaData = {
        // 默认是灰色不能预览,因为默认content-type 是 application/octet-stream
        // 'Content-Type': 'application/octet-stream',
        'Content-Type': stream.mimeType
      }
      const etag = await this.minioClient.putObject(bucketName, objectName, stream, size, metaData)
      console.log('step 2', etag)

      /* 组装文件地址 */
      const { url: obsUrl } = 'https://www.example.com'
      const url = `${obsUrl}/${bucketName}/${objectName}`
      console.log('step 3', url)

      this.success({ ctx, data: { url } });
    } catch (err) {
      console.log(err)
      this.fail({ ctx, message: err });
    }
  }
}

module.exports = FileObsController;
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部