全棧開發實戰——MinIO

介紹

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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章