Minio 文件服務(1)—— Minio部署使用及存儲機制分析
Minio 文件服務(2)—— Minio用Nginx做負載均衡
本文參考Minio官方文檔,使用細節(jié)里面說的很詳細住闯,本文主要講解文檔中較少涉及的Minio存儲機制默终。以及我在使用中部署和使用Java SDK訪問的過程。
簡介
Minio 是一個基于Apache License v2.0開源協(xié)議的
對象存儲服務
屁擅。它兼容亞馬遜S3云存儲服務接口偿凭,非常適合于存儲大容量非結(jié)構(gòu)化
的數(shù)據(jù),例如圖片派歌、視頻弯囊、日志文件、備份數(shù)據(jù)和容器/虛擬機鏡像等胶果,而一個對象文件可以是任意大小匾嘱,從幾kb到最大5T不等。
Minio是一個非常輕量的服務,可以很簡單的和其他應用的結(jié)合早抠,類似 NodeJS, Redis 或者 MySQL霎烙。
存儲機制
Minio使用糾刪碼
erasure code
和校驗和checksum
來保護數(shù)據(jù)免受硬件故障和無聲數(shù)據(jù)損壞。 即便丟失一半數(shù)量(N/2)的硬盤蕊连,仍然可以恢復數(shù)據(jù)悬垃。
糾刪碼
糾刪碼是一種恢復丟失和損壞數(shù)據(jù)的數(shù)學算法,目前甘苍,糾刪碼技術在分布式存儲系統(tǒng)中的應用主要有三類尝蠕,陣列糾刪碼(Array Code: RAID5、RAID6等)载庭、RS(Reed-Solomon)里德-所羅門類糾刪碼和LDPC(LowDensity Parity Check Code)低密度奇偶校驗糾刪碼看彼。Erasure Code是一種編碼技術,它可以將n份原始數(shù)據(jù)囚聚,增加m份數(shù)據(jù)靖榕,并能通過n+m份中的任意n份數(shù)據(jù),還原為原始數(shù)據(jù)靡挥。即如果有任意小于等于m份的數(shù)據(jù)失效序矩,仍然能通過剩下的數(shù)據(jù)還原出來。
Minio采用Reed-Solomon code將對象拆分成N/2數(shù)據(jù)和N/2 奇偶校驗塊跋破。因此下面主要講解RS類糾刪碼簸淀。
RS code編碼數(shù)據(jù)恢復原理:
RS編碼以word為編碼和解碼單位瓶蝴,大的數(shù)據(jù)塊拆分到字長為w(取值一般為8或者16位)的word,然后對word進行編解碼租幕。 數(shù)據(jù)塊的編碼原理與word編碼原理相同舷手,后文中以word為例說明,變量Di, Ci將代表一個word劲绪。
把輸入數(shù)據(jù)視為向量D=(D1男窟,D2,..., Dn), 編碼后數(shù)據(jù)視為向量(D1, D2,..., Dn, C1, C2,.., Cm)贾富,RS編碼可視為如下(圖1)所示矩陣運算歉眷。
圖1最左邊是編碼矩陣(或稱為生成矩陣、分布矩陣颤枪,Distribution Matrix)汗捡,編碼矩陣需要滿足任意n*n子矩陣可逆。為方便數(shù)據(jù)存儲畏纲,編碼矩陣上部是單位陣(n行n列)扇住,下部是m行n列矩陣。下部矩陣可以選擇范德蒙德矩陣或柯西矩陣盗胀。
RS最多能容忍m個數(shù)據(jù)塊被刪除艘蹋。 數(shù)據(jù)恢復的過程如下:
(1)假設D1、D4票灰、C2丟失女阀,從編碼矩陣中刪掉丟失的數(shù)據(jù)塊/編碼塊對應的行。(圖2米间、3)
(2)由于B' 是可逆的强品,記B'的逆矩陣為 (B'^-1),則B' * (B'^-1) = I 單位矩陣屈糊。兩邊左乘B' 逆矩陣。 (圖4琼了、5)
(3)得到如下原始數(shù)據(jù)D的計算公式 逻锐。
(4)對D重新編碼,可得到丟失的編碼碼
實踐
Minio采用Reed-Solomon code將對象拆分成N/2數(shù)據(jù)和N/2 奇偶校驗塊雕薪。 這就意味著如果是12塊盤昧诱,一個對象會被分成6個數(shù)據(jù)塊、6個奇偶校驗塊所袁,可以丟失任意6塊盤(不管其是存放的數(shù)據(jù)塊還是奇偶校驗塊)盏档,仍可以從剩下的盤中的數(shù)據(jù)進行恢復。
以下是我在4個節(jié)點上部署的集群燥爷,部署代碼如下面多節(jié)點部署所示蜈亩。
1懦窘、我在minio服務中上傳了一個hello.txt文件,內(nèi)容為:hello,how are you?\n 共20個字符稚配。
2畅涂、然后在對應的bucket里會生成一個名為hello.txt的文件夾,文件夾里有2個文件道川,查看4個節(jié)點的part.1文件午衰,會發(fā)現(xiàn)其中2個文件均分存放了原始數(shù)據(jù)hello,how are you?\n ,另外2個文件是亂碼冒萄,但是根據(jù)上述分析應該存的就是奇偶校驗塊臊岸,充當編碼矩陣的作用。因此part.1文件是存儲就刪碼的尊流。
(數(shù)據(jù)塊和奇偶校驗塊所存儲的節(jié)點位置不是固定不變的)
3帅戒、而另一個json文件中的內(nèi)容如下,對比了4個節(jié)點xl.json文件的差別奠旺,發(fā)現(xiàn)只有index和checksum中的hash值不同蜘澜。因此這個json文件是存儲校驗和的。保護數(shù)據(jù)免受無聲數(shù)據(jù)損壞响疚。
模擬數(shù)據(jù)丟失的情況
新建一個happy.txt文件鄙信,上傳至文件服務,在服務器的4個節(jié)點上可以看見該文件生成了一個part.1和xl.json文件忿晕。
第一步刪除005號服務器上的數(shù)據(jù)塊装诡,下載該文件,可讀践盼;
第二步刪除004號服務器上的數(shù)據(jù)塊鸦采,下載該文件,可讀咕幻;
第二步刪除003號服務器上的數(shù)據(jù)塊渔伯,下載該文件,不可讀肄程,得到空白文件锣吼;因為丟失的硬盤數(shù)量大于N/2,不可恢復健康數(shù)據(jù)蓝厌。
模擬checksum丟失的情況
新建一個sing.txt文件玄叠,上傳至文件服務,在服務器的4個節(jié)點上可以看見該文件生成了一個part.1和xl.json文件拓提。
第一步刪除005號服務器上的校驗塊读恃,下載該文件,可讀;
第二步刪除004號服務器上的校驗塊寺惫,下載該文件疹吃,可讀;
第二步刪除003號服務器上的校驗塊肌蜻,下載該文件互墓,不可讀,頁面報異常蒋搜,返回頁面后該文件已不存在篡撵。
再看服務器中但存儲,文件名變?yōu)?CORRUPTED后綴豆挽。文件被損壞育谬。
單機Minio服務存在單點故障,相反帮哈,如果是一個N節(jié)點的分布式Minio,只要有N/2節(jié)點在線膛檀,你的數(shù)據(jù)就是安全的。不過你需要至少有N/2+1個節(jié)點 Quorum 來創(chuàng)建新的對象娘侍。
例如咖刃,一個8節(jié)點的Minio集群,每個節(jié)點一塊盤憾筏,就算4個節(jié)點宕機嚎杨,這個集群仍然是可讀的,不過你需要5個節(jié)點才能寫數(shù)據(jù)氧腰。
部署
單節(jié)點
(容器部署)
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
#要創(chuàng)建具有永久存儲的Minio容器枫浙,您需要將本地持久目錄從主機操作系統(tǒng)映射到虛擬配置~/.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
多節(jié)點
分布式搭建的流程和單節(jié)點基本一樣,Minio服務基于命令行傳入的參數(shù)自動切換成單機模式還是分布式模式古拴。
分布式Minio單租戶存在最少4個盤最多16個盤的限制(受限于糾刪碼)箩帚。這種限制確保了Minio的簡潔,同時仍擁有伸縮性黄痪。如果你需要搭建一個多租戶環(huán)境紧帕,你可以輕松的使用編排工具(Kubernetes)來管理多個Minio實例。
糾刪碼 (多塊硬盤 / 服務)
項目 | 參數(shù) |
---|---|
最大驅(qū)動器數(shù)量 | 16 |
最小驅(qū)動器數(shù)量 | 4 |
讀仲裁 | N / 2 |
寫仲裁 | N / 2+1 |
(多節(jié)點部署如果要使用容器需要用docker swarm 和K8s桅打,文檔中有介紹焕参,本文重點不在此因此我直接在4個服務器上安裝了Minio,直接運行即可油额。Minio服務基于命令行傳入的參數(shù)自動切換成單機模式還是分布式模式
,啟動一個分布式Minio實例刻帚,你只需要把硬盤位置做為參數(shù)傳給minio server命令即可潦嘶,然后,你需要在所有其它節(jié)點運行同樣的命令崇众。)
部署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);
// 檢查存儲桶是否已經(jīng)存在
if(minioClient.bucketExists("ota")) {
log.info("Bucket already exists.");
} else {
// 創(chuàng)建一個名為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);
}
}
}