Minio 文件服務(1)—— Minio部署使用及存儲機制分析
Minio 文件服務(2)—— Minio用Nginx做負載均衡
本文參考Minio官方文檔,使用細節里面說的很詳細,本文主要講解文檔中較少涉及的Minio存儲機制。以及我在使用中部署和使用Java SDK訪問的過程。
簡介
Minio 是一個基于Apache License v2.0開源協議的
對象存儲服務
。它兼容亞馬遜S3云存儲服務接口,非常適合于存儲大容量非結構化
的數據,例如圖片、視頻、日志文件、備份數據和容器/虛擬機鏡像等,而一個對象文件可以是任意大小,從幾kb到最大5T不等。
Minio是一個非常輕量的服務,可以很簡單的和其他應用的結合,類似 NodeJS, Redis 或者 MySQL。
存儲機制
Minio使用糾刪碼
erasure code
和校驗和checksum
來保護數據免受硬件故障和無聲數據損壞。 即便丟失一半數量(N/2)的硬盤,仍然可以恢復數據。
糾刪碼
糾刪碼是一種恢復丟失和損壞數據的數學算法,目前,糾刪碼技術在分布式存儲系統中的應用主要有三類,陣列糾刪碼(Array Code: RAID5、RAID6等)、RS(Reed-Solomon)里德-所羅門類糾刪碼和LDPC(LowDensity Parity Check Code)低密度奇偶校驗糾刪碼。Erasure Code是一種編碼技術,它可以將n份原始數據,增加m份數據,并能通過n+m份中的任意n份數據,還原為原始數據。即如果有任意小于等于m份的數據失效,仍然能通過剩下的數據還原出來。
Minio采用Reed-Solomon code將對象拆分成N/2數據和N/2 奇偶校驗塊。因此下面主要講解RS類糾刪碼。
RS code編碼數據恢復原理:
RS編碼以word為編碼和解碼單位,大的數據塊拆分到字長為w(取值一般為8或者16位)的word,然后對word進行編解碼。 數據塊的編碼原理與word編碼原理相同,后文中以word為例說明,變量Di, Ci將代表一個word。
把輸入數據視為向量D=(D1,D2,..., Dn), 編碼后數據視為向量(D1, D2,..., Dn, C1, C2,.., Cm),RS編碼可視為如下(圖1)所示矩陣運算。
圖1最左邊是編碼矩陣(或稱為生成矩陣、分布矩陣,Distribution Matrix),編碼矩陣需要滿足任意n*n子矩陣可逆。為方便數據存儲,編碼矩陣上部是單位陣(n行n列),下部是m行n列矩陣。下部矩陣可以選擇范德蒙德矩陣或柯西矩陣。
RS最多能容忍m個數據塊被刪除。 數據恢復的過程如下:
(1)假設D1、D4、C2丟失,從編碼矩陣中刪掉丟失的數據塊/編碼塊對應的行。(圖2、3)
(2)由于B' 是可逆的,記B'的逆矩陣為 (B'^-1),則B' * (B'^-1) = I 單位矩陣。兩邊左乘B' 逆矩陣。 (圖4、5)
(3)得到如下原始數據D的計算公式 。
(4)對D重新編碼,可得到丟失的編碼碼
實踐
Minio采用Reed-Solomon code將對象拆分成N/2數據和N/2 奇偶校驗塊。 這就意味著如果是12塊盤,一個對象會被分成6個數據塊、6個奇偶校驗塊,可以丟失任意6塊盤(不管其是存放的數據塊還是奇偶校驗塊),仍可以從剩下的盤中的數據進行恢復。
以下是我在4個節點上部署的集群,部署代碼如下面多節點部署所示。
1、我在minio服務中上傳了一個hello.txt文件,內容為:hello,how are you?\n 共20個字符。
2、然后在對應的bucket里會生成一個名為hello.txt的文件夾,文件夾里有2個文件,查看4個節點的part.1文件,會發現其中2個文件均分存放了原始數據hello,how are you?\n ,另外2個文件是亂碼,但是根據上述分析應該存的就是奇偶校驗塊,充當編碼矩陣的作用。因此part.1文件是存儲就刪碼的。
(數據塊和奇偶校驗塊所存儲的節點位置不是固定不變的)
3、而另一個json文件中的內容如下,對比了4個節點xl.json文件的差別,發現只有index和checksum中的hash值不同。因此這個json文件是存儲校驗和的。保護數據免受無聲數據損壞。
模擬數據丟失的情況
新建一個happy.txt文件,上傳至文件服務,在服務器的4個節點上可以看見該文件生成了一個part.1和xl.json文件。
第一步刪除005號服務器上的數據塊,下載該文件,可讀;
第二步刪除004號服務器上的數據塊,下載該文件,可讀;
第二步刪除003號服務器上的數據塊,下載該文件,不可讀,得到空白文件;因為丟失的硬盤數量大于N/2,不可恢復健康數據。
模擬checksum丟失的情況
新建一個sing.txt文件,上傳至文件服務,在服務器的4個節點上可以看見該文件生成了一個part.1和xl.json文件。
第一步刪除005號服務器上的校驗塊,下載該文件,可讀;
第二步刪除004號服務器上的校驗塊,下載該文件,可讀;
第二步刪除003號服務器上的校驗塊,下載該文件,不可讀,頁面報異常,返回頁面后該文件已不存在。
再看服務器中但存儲,文件名變為.CORRUPTED后綴。文件被損壞。
單機Minio服務存在單點故障,相反,如果是一個N節點的分布式Minio,只要有N/2節點在線,你的數據就是安全的。不過你需要至少有N/2+1個節點 Quorum 來創建新的對象。
例如,一個8節點的Minio集群,每個節點一塊盤,就算4個節點宕機,這個集群仍然是可讀的,不過你需要5個節點才能寫數據。
部署
單節點
(容器部署)
docker pull minio/minio
#在Docker中運行Minio單點模式
docker run -p 9000:9000 -e MINIO_ACCESS_KEY=sunseaiot -e MINIO_SECRET_KEY=sunseaiot minio/minio server /data
#要創建具有永久存儲的Minio容器,您需要將本地持久目錄從主機操作系統映射到虛擬配置~/.minio 并導出/data目錄
#建立外掛文件夾 /Users/hbl/dockersp/volume/minio/
docker run -p 9000:9000 -e MINIO_ACCESS_KEY=sunseaiot -e MINIO_SECRET_KEY=sunseaiot -v /Users/hbl/dockersp/volume/minio/data:/data -v /Users/hbl/dockersp/volume/minio/config:/root/.minio minio/minio server /data
多節點
分布式搭建的流程和單節點基本一樣,Minio服務基于命令行傳入的參數自動切換成單機模式還是分布式模式。
分布式Minio單租戶存在最少4個盤最多16個盤的限制(受限于糾刪碼)。這種限制確保了Minio的簡潔,同時仍擁有伸縮性。如果你需要搭建一個多租戶環境,你可以輕松的使用編排工具(Kubernetes)來管理多個Minio實例。
糾刪碼 (多塊硬盤 / 服務)
項目 | 參數 |
---|---|
最大驅動器數量 | 16 |
最小驅動器數量 | 4 |
讀仲裁 | N / 2 |
寫仲裁 | N / 2+1 |
(多節點部署如果要使用容器需要用docker swarm 和K8s,文檔中有介紹,本文重點不在此因此我直接在4個服務器上安裝了Minio,直接運行即可。Minio服務基于命令行傳入的參數自動切換成單機模式還是分布式模式
,啟動一個分布式Minio實例,你只需要把硬盤位置做為參數傳給minio server命令即可,然后,你需要在所有其它節點運行同樣的命令。)
部署4主機,每機單塊磁盤(drive)
export MINIO_ACCESS_KEY=123456
export MINIO_SECRET_KEY=123456
./minio server http://192.168.8.110/export1 \
http://192.168.8.111/export2 \
http://192.168.8.112/export3 \
http://192.168.8.113/export4
部署4主機,每機2塊磁盤(drives)
export MINIO_ACCESS_KEY=123456
export MINIO_SECRET_KEY=123456
./minio server http://192.168.8.110/export1 http://192.168.1.110/export2 \
http://192.168.8.111/export1 http://192.168.1.111/export2 \
http://192.168.8.112/export1 http://192.168.1.112/export2 \
http://192.168.8.113/export1 http://192.168.1.113/export2
后臺運行
由于不是用docker部署的,所以需要將進程加入后臺運行。使用nohup指令。
export MINIO_ACCESS_KEY=SunseaIoT2018!
export MINIO_SECRET_KEY=SunseaIoT2018!
nohup ./minio server http://192.168.8.110/minio1 \
http://192.168.8.111/minio2 \
http://192.168.8.112/minio3 \
http://192.168.8.113/minio4 > out.file 2>&1 &
使用
部署好Minio服務后可以通過瀏覽器訪問。輸入設置好的用戶名和密碼即可進行操作。
Java SDK訪問Minio服務
package com.minio.client;
import io.minio.MinioClient;
import io.minio.errors.MinioException;
import lombok.extern.slf4j.Slf4j;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@Slf4j
public class FileUploader {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException {
try {
MinioClient minioClient = new MinioClient("https://minio.sunseaiot.com", "sunseaiot", "sunseaiot",true);
// 檢查存儲桶是否已經存在
if(minioClient.bucketExists("ota")) {
log.info("Bucket already exists.");
} else {
// 創建一個名為ota的存儲桶
minioClient.makeBucket("ota");
log.info("create a new bucket.");
}
//獲取下載文件的url,直接點擊該url即可在瀏覽器中下載文件
String url = minioClient.presignedGetObject("ota","hello.txt");
log.info(url);
//獲取上傳文件的url,這個url可以用Postman工具測試,在body里放入需要上傳的文件即可
String url2 = minioClient.presignedPutObject("ota","ubuntu.tar");
log.info(url2);
// 下載文件到本地
minioClient.getObject("ota","hello.txt", "/Users/hbl/hello2.txt");
log.info("get");
// 使用putObject上傳一個本地文件到存儲桶中。
minioClient.putObject("ota","tenant2/hello.txt", "/Users/hbl/hello.txt");
log.info("/Users/hbl/hello.txt is successfully uploaded as hello.txt to `task1` bucket.");
} catch(MinioException e) {
log.error("Error occurred: " + e);
}
}
}