【技術解析】如何用Docker實現SequoiaDB集羣的快速部署

  1. 背景
    以Docker和Rocket爲代表的容器技術現在正變得越來越流行,它改變着公司和用戶創建、發佈、運行分佈式應用的方式,在未來5年將給雲計算行業帶來它應有的價值。它的誘人之處在於:
    1)資源獨立、隔離
    資源隔離是雲計算平臺的最基本需求。Docker通過Linux namespace, cgroup限制了硬件資源與軟件運行環境,與宿主機上的其他應用實現了隔離,做到了互不影響。不同應用或服務以“集裝箱”(container)爲單位裝“船”或卸“船”,“集裝箱船”(運行container的宿主機或集羣 )上,數千數萬個“集裝箱”排列整齊,不同公司、不同種類的“貨物”(運行應用所需的程序、組件、運行環境、依賴)保持獨立。
    2)環境的一致性
    開發工程師完成應用開發後build一個docker image,基於這個image創建的container像是一個集裝箱,裏面打包了各種“散件貨物”(運行應用所需的程序、組件、運行環境、依賴)。無論這個集裝箱在哪裏:開發環境、測試環境、生產環境,都可以確保集裝箱裏面的“貨物”種類與個數完全相同,軟件包不會在測試環境缺失,環境變量不會在生產環境忘記配置,開發環境與生產環境不會因爲安裝了不同版本的依賴導致應用運行異常。這樣的一致性得益於“發貨”(build docker image)時已經密封到“集裝箱”中,而每一個環節都是在運輸這個完整的、不需要拆分合並的“集裝箱”。
    3)輕量化
    相比傳統的虛擬化技術(VM),使用docker在cpu, memory, disk IO, network IO上的性能損耗都有同樣水平甚至更優的表現。Container的快速創建、啓動、銷燬受到很多讚譽。
    4)Build Once, Run Everywhere
    這個特性着實吸引了很多人,“貨物”(應用)在“汽車”、“火車”、“輪船”(私有云、公有云等服務)之間遷移交換時,只需要遷移符合標準規格和裝卸方式的“集裝箱”(docker container),削減了耗時費力的人工“裝卸”(上線、下線應用),帶來的是巨大的時間人力成本節約。這使未來僅有少數幾個運維人員運維超大規模裝載線上應用的容器集羣成爲可能,如同60年代後少數幾個機器操作員即可在幾小時內連裝帶卸完一艘萬級集裝箱船。
    容器技術現在也被廣泛應用於數據庫領域。它的“Build Once, Run Everywhere”的特性大大減少了花在安裝配置數據庫環境上的時間,因爲即使對於從事數據庫多年的DBA而言,安裝配置數據庫環境依舊是一項看似簡單但卻經常不順利的工作。當然,容器技術的其他優勢也被很好的應用在數據庫的使用中。
    SequoiaDB作爲一款優秀的國產分佈式NewSQL數據庫,已經得到了越來越多用戶的認可。本文以Docker爲例,着重介紹如何用Dockerfile快速構建SequoiaDB鏡像,以及如何利用容器快速搭建和啓動SequoiaDB集羣供應用系統使用。

  2. 構建SequoiaDB鏡像
    如何安裝docker以及配置鏡像倉庫不是本文的重點,網上有很多相關的技術文章。需要指出的是本文采用阿里雲鏡像倉庫,因爲將鏡像上傳到Docker官方倉庫的速度實在不敢恭維。如何註冊和使用阿里雲鏡像倉庫,可以參考文章(http://www.jb51.net/article/123101.htm)。

STEP 1:創建Dockerfile,內容如下,只需要幾行簡單的指令即可

*# Sequoiadb DOCKERFILES PROJECT
--------------------------
This is the Dockerfile for Sequoiadb 2.8.4

REQUIRED FILES TO BUILD THIS IMAGE
----------------------------------
(1) sequoiadb-2.8.4-linuxx8664-enterprise-installer.run
(2) installSDB.sh

HOW TO BUILD THIS IMAGE
-----------------------
Put all downloaded files in the same directory as this Dockerfile
Run: 
$ sudo docker build -t sequoiadb:2.8.4 . 

Pull base image
FROM ubuntu

Environment variables required for this build
ENV INSTALLBINFILE="sequoiadb-2.8.4-linuxx8664-enterprise-installer.run" \
INSTALLSDBSCRIPT="installSDB.sh" \
INSTALLDIR="/opt/sequoiadb"

Copy binaries
ADD $INSTALLBINFILE $INSTALLSDBSCRIPT $INSTALLDIR/

Install SDB software binaries
RUN chmod 755 $INSTALLDIR/$INSTALLSDBSCRIPT \
&& $INSTALLDIR/$INSTALLSDBSCRIPT \
&& rm $INSTALLDIR/$INSTALLSDBSCRIPT**

其中installSDB.sh腳本內容如下:

chmod 755 $INSTALL_DIR/$INSTALL_BIN_FILE
$INSTALL_DIR/$INSTALL_BIN_FILE --mode unattended 
rm $INSTALL_DIR/$INSTALL_BIN_FILE
echo 'service sdbcm start' >> /root/.bashrc

需要注意的是本例採用Sequoiadb企業版2.8.4,您也可以從巨杉官網下載社區版(選擇tar包,下載然後解壓),替換本例中的介質名稱。
巨杉官網下載地址:http://download.sequoiadb.com/cn/

STEP 2:創建鏡像
root用戶執行:
docker build -t sequoiadb:2.8.4 .
如果是普通用戶,需要使用sudo:
sudo docker build -t sequoiadb:2.8.4 .

STEP3:登陸阿里雲鏡像倉庫
docker login --username=xxx registry.cn-hangzhou.aliyuncs.com
其中xxx爲您在阿里雲註冊的賬號。

STEP4:查看本地sequoiadb鏡像id
docker images

STEP5:標記本地鏡像,將其歸入阿里雲倉庫
docker tag 04dc528f2a6f registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb:latest
其中04dc528f2a6f是筆者本地sequoiadb鏡像id,新的標記格式有一定要求,registry.cn-hangzhou.aliyuncs.com爲阿里雲倉庫地址,508mars是筆者在阿里雲的用戶名,sequoiadb是鏡像名,latest是tag。

STEP6:提交sequoiadb鏡像到鏡像庫
docker push registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb:latest

  1. 利用容器啓動SequoiaDB集羣
    Docker的網絡默認採用bridge模式,採用bridge模式的容器有如下特點:
    1)同一宿主機的容器之間可以互相ping通
    2)不同宿主機的容器之間互相ping不同
    但是SequoiaDB集羣要求所有節點之間是可以互通的,所以如果運行SequoiaDB的容器跑在不同宿主機上,docker的默認網絡模式顯然不合適。有很多種方法可以解決不同宿主機容器之間的連通性問題,本文只介紹weave虛擬網絡這個解決方案,因爲weave同時提供了一個DNS server的功能,有了該功能,在利用容器部署SequoiaDB集羣時不再需要修改各個容器內部的/etc/hosts,大大簡化了自動化部署的步驟。

STEP1:安裝weave網絡

curl -s -L git.io/weave -o /usr/local/bin/weave
chmod a+x /usr/local/bin/weave

需要在所有宿主機安裝,筆者採用了三臺虛擬機作爲宿主機:sdb1, sdb2和sdb3。

STEP2:啓動weave網絡
weave launch
第一次啓動時會下載weave鏡像。

STEP3:從阿里雲倉庫下載sequoiadb鏡像
docker pull registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb

STEP4:在所有宿主機創建docker的掛載卷

cd /home/sdbadmin
mkdir -p data/disk1 data/disk2 data/disk3
mkdir -p conf/local
chmod -R 777 data
chmod -R 777 conf

掛載卷的位置可以自定義,但總的來說需要創建兩大類掛載卷,一類用來存放集合數據,如本例中的data/disk1, data/disk2, data/disk3,一類用來存放節點配置信息,如本例中的conf/local。這樣即使容器被誤刪了,依舊可以啓動一個新容器來扮演被誤刪的容器的角色。

STEP5:啓動容器
sdb1:

weave stop
weave launch
eval $(weave env)
docker run -dit --name sdbserver1 -p 11810:11810 -v /home/sdbadmin/data:/data -v /home/sdbadmin/conf/local:/opt/sequoiadb/conf/local registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb

sdb2:

weave stop
weave launch 192.168.17.132
eval $(weave env)
docker run -dit --name sdbserver2 -p 11810:11810 -v /home/sdbadmin/data:/data -v /home/sdbadmin/conf/local:/opt/sequoiadb/conf/local registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb

sdb3:

weave stop
weave launch 192.168.17.132
eval $(weave env)
docker run -dit --name sdbserver3 -p 11810:11810 -v /home/sdbadmin/data:/data -v /home/sdbadmin/conf/local:/opt/sequoiadb/conf/local registry.cn-hangzhou.aliyuncs.com/508mars/sequoiadb

其中192.168.17.132是sdb1的IP地址,11810是對外暴露的集羣訪問端口。宿主機存放節點配置信息的卷必須掛到容器的/opt/sequoiadb/conf/local目錄,存放表數據的卷可以掛載到用戶自定義的目錄,但是集羣一旦創建後,不可更改。啓動容器的時候必須指定機器名,因爲在構建完集羣后,機器名會被保存在SequoiaDB的系統表中,節點的機器名與系統表中不一致會導致無法加入到集羣。在使用weave的場景下,建議使用--name選項,不要使用--hostname設置機器名。後者會阻止weave將機器名添加到DNS服務器,weave會自動根據--name的值來設置機器名,同時在機器名後增加weave.local域名,並添加到的DNS服務器。

STEP6:將創建SequoiaDB集羣的腳本拷貝到容器中
docker cp create_cluster.js sdbserver1:/data

create_cluster.js內容如下:

var array_hosts = ["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"];
var array_dbroot = ["/data/disk1/sequoiadb/database","/data/disk2/sequoiadb/database","/data/disk3/sequoiadb/database"];
var port_sdbcm = "11790";
var port_temp_coord = "18888";
var cataloggroup = {gname:"SYSCatalogGroup", gport:"11820", ghosts:["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"]};
var array_coordgroups = [
        {gname:"SYSCoord", gport:"11810", ghosts:["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"]}
];
var array_datagroups = [
        {gname:"dg1", gport:"11830", ghosts:["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"], goptions:{transactionon:true}}
       ,{gname:"dg2", gport:"11840", ghosts:["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"], goptions:{transactionon:true}}
       ,{gname:"dg3", gport:"11850", ghosts:["sdbserver1.weave.local", "sdbserver2.weave.local", "sdbserver3.weave.local"], goptions:{transactionon:true}}
];
var array_domains = [
        {dname:"allgroups", dgroups:["dg1", "dg2", "dg3"], doptions:{AutoSplit:true}}
];

println("啓動臨時協調節點");
var oma = new Oma(array_coordgroups[0].ghosts[0], port_sdbcm);
oma.createCoord(port_temp_coord, array_dbroot[0]+"/coord/"+port_temp_coord);
oma.startNode(port_temp_coord);

println("創建編目節點組:"+cataloggroup.ghosts[0]+"   "+cataloggroup.gport+"    "+array_dbroot[0]+"/cata/"+cataloggroup.gport);
var db = new Sdb(array_coordgroups[0].ghosts[0], port_temp_coord);
db.createCataRG(cataloggroup.ghosts[0], cataloggroup.gport, array_dbroot[0]+"/cata/"+cataloggroup.gport);
var cataRG = db.getRG("SYSCatalogGroup");
for (var i in cataloggroup.ghosts) {
        if (i==0) {continue;}
    println("創建編目節點: "+cataloggroup.ghosts[i]+"  "+cataloggroup.gport+"    "+array_dbroot[0]+"/cata/"+cataloggroup.gport);
        var catanode = cataRG.createNode(cataloggroup.ghosts[i], cataloggroup.gport, array_dbroot[0]+"/cata/"+cataloggroup.gport);
        catanode.start();
}

println("創建協調節點組");
var db = new Sdb(array_coordgroups[0].ghosts[0], port_temp_coord);
var coordRG = db.createCoordRG();
for (var i in array_coordgroups) {
        for (var j in array_coordgroups[i].ghosts) {
                println("創建協調節點組:"+array_coordgroups[i].ghosts[j]+"    "+array_coordgroups[i].gport+"    "+array_dbroot[0]+"/coord/"+array_coordgroups[i].gport);
                coordRG.createNode(array_coordgroups[i].ghosts[j], array_coordgroups[i].gport, array_dbroot[0]+"/coord/"+array_coordgroups[i].gport);
        }
}
coordRG.start();

println("刪除臨時協調節點")
var oma = new Oma(array_coordgroups[0].ghosts[0], port_sdbcm);
oma.removeCoord(port_temp_coord);

println("創建數據節點組")
var db = new Sdb(array_coordgroups[0].ghosts[0], array_coordgroups[0].gport);
var k=0;
for (var i in array_datagroups) {
        var dataRG = db.createRG(array_datagroups[i].gname);
        for (var j in array_datagroups[i].ghosts) {
                println("創建數據節點:"+array_datagroups[i].gname+"    "+array_datagroups[i].ghosts[j]+"   "+array_datagroups[i].gport+"    "+array_dbroot[k]+"/data/"+array_datagroups[i].gport+"    "+array_datagroups[i].goptions)
                dataRG.createNode(array_datagroups[i].ghosts[j], array_datagroups[i].gport, array_dbroot[k]+"/data/"+array_datagroups[i].gport, array_datagroups[i].goptions);
        }
        dataRG.start();
        k++;
}

println("創建域");
var db = new Sdb(array_coordgroups[0].ghosts[0], array_coordgroups[0].gport);
for (var i in array_domains) {
        println("創建域:"+array_domains[i].dname+"   "+array_domains[i].dgroups+"    "+array_domains[i].doptions)
        db.createDomain(array_domains[i].dname, array_domains[i].dgroups, array_domains[i].doptions );
}

STEP7:創建SequoiaDB集羣
docker exec sdbserver1 su - sdbadmin -c "sdb -f /data/create_cluster.js"

至此SequoiaDB集羣創建並啓動完成,後面再啓動容器的時候集羣會自動啓動。

  1. 結論
    SequoiaDB利用容器技術很好的實現了集羣快速部署,大大簡化了初學者安裝部署的難度。後期筆者還會在SequoiaDB鏡像製作上做一些優化,因爲目前做出來的鏡像有點大,主要根源是採用ADD或COPY命令將安裝介質拷貝到Docker容器中會生成一個新的鏡像image1,最終生成的鏡像image2中雖然刪除了安裝介質,但是它在image1之上,所以image2的大小依舊包含安裝介質。最好採用ADD拷貝tar包(ADD會自動解壓)或者採用類似如下的方式:
    RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.xz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

<END>

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