溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

如何通過(guò)Mesos、Docker和Go創(chuàng)建一個(gè)分布式系統(tǒng)

發(fā)布時(shí)間:2021-11-10 18:15:02 來(lái)源:億速云 閱讀:153 作者:柒染 欄目:大數(shù)據(jù)

今天就跟大家聊聊有關(guān)如何通過(guò)Mesos、Docker和Go創(chuàng)建一個(gè)分布式系統(tǒng),可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

時(shí)下,對(duì)于大部分IT玩家來(lái)說(shuō),Docker和Mesos都是熟悉和陌生的:熟悉在于這兩個(gè)詞無(wú)疑已成為大家討論的焦點(diǎn),而陌生在于這兩個(gè)技術(shù)并未在生產(chǎn)環(huán)境得到廣泛使用,因此很多人仍然不知道它們究竟有什么優(yōu)勢(shì),或者能干什么。

構(gòu)建一個(gè)分布式系統(tǒng)是很困難的。它需要可擴(kuò)展性、容錯(cuò)性、高可用性、一致性、可伸縮以及高效。為了達(dá)到這些目的,分布式系統(tǒng)需要很多復(fù)雜的組件以一種復(fù)雜的方式協(xié)同工作。例如,Apache Hadoop在大型集群上并行處理TB級(jí)別的數(shù)據(jù)集時(shí),需要依賴(lài)有著高容錯(cuò)的文件系統(tǒng)(HDFS)來(lái)達(dá)到高吞吐量。

在之前,每一個(gè)新的分布式系統(tǒng),例如Hadoop和Cassandra,都需要構(gòu)建自己的底層架構(gòu),包括消息處理、存儲(chǔ)、網(wǎng)絡(luò)、容錯(cuò)性和可伸縮性。慶幸的是,像Apache Mesos這樣的系統(tǒng),通過(guò)給分布式系統(tǒng)的關(guān)鍵構(gòu)建模塊提供類(lèi)似操作系統(tǒng)的管理服務(wù),簡(jiǎn)化了構(gòu)建和管理分布式系統(tǒng)的任務(wù)。Mesos抽離了CPU、存儲(chǔ)和其它計(jì)算資源,因此開(kāi)發(fā)者開(kāi)發(fā)分布式應(yīng)用程序時(shí)能夠?qū)⒄麄€(gè)數(shù)據(jù)中心集群當(dāng)做一臺(tái)巨型機(jī)對(duì)待。

構(gòu)建在Mesos上的應(yīng)用程序被稱(chēng)為框架,它們能解決很多問(wèn)題:Apache Spark,一種流行的集群式數(shù)據(jù)分析工具;Chronos,一個(gè)類(lèi)似cron的具有容錯(cuò)性的分布式scheduler,這是兩個(gè)構(gòu)建在Mesos上的框架的例子。構(gòu)建框架可以使用多種語(yǔ)言,包括C++,Go,Python,Java,Haskell和 Scala。

在分布式系統(tǒng)用例上,比特幣開(kāi)采就是一個(gè)很好的例子。比特幣將為生成 acceptable hash 的挑戰(zhàn)轉(zhuǎn)為驗(yàn)證一塊事務(wù)的可靠性??赡苄枰獛资?,單臺(tái)筆記本電腦挖一塊可能需要花費(fèi)超過(guò)150年。結(jié)果是,有許多的“采礦池”允許采礦者將他們的計(jì)算資源聯(lián)合起來(lái)以加快挖礦速度。Mesosphere的一個(gè)實(shí)習(xí)生,Derek,寫(xiě)了一個(gè)比特幣開(kāi)采框架(https://github.com/derekchiang/Mesos-Bitcoin-Miner),利用集群資源的優(yōu)勢(shì)來(lái)做同樣的事情。在接下來(lái)的內(nèi)容中,會(huì)以他的代碼為例。

1個(gè)Mesos框架有1個(gè)scheduler 和1個(gè)executor組成。scheduler 和Mesos master通信并決定運(yùn)行什么任務(wù),而executor 運(yùn)行在slaves上面,執(zhí)行實(shí)際任務(wù)。大多數(shù)的框架實(shí)現(xiàn)了自己的scheduler,并使用1個(gè)由Mesos提供的標(biāo)準(zhǔn)executors。當(dāng)然,框架也可以自己定制executor。在這個(gè)例子中即會(huì)編寫(xiě)定制的scheduler,并使用標(biāo)準(zhǔn)命令執(zhí)行器(executor)運(yùn)行包含我們比特幣服務(wù)的Docker鏡像。

對(duì)這里的scheduler來(lái)說(shuō),需要運(yùn)行的有兩種任務(wù)——one miner server task and multiple miner worker tasks。server會(huì)和一個(gè)比特幣采礦池通信,并給每個(gè)worker分配blocks。Worker會(huì)努力工作,即開(kāi)采比特幣。

任務(wù)實(shí)際上被封裝在executor框架中,因此任務(wù)運(yùn)行意味著告訴Mesos master在其中一個(gè)slave上面啟動(dòng)一個(gè)executor。由于這里使用的是標(biāo)準(zhǔn)命令執(zhí)行器(executor),因此可以指定任務(wù)是二進(jìn)制可執(zhí)行文件、bash腳本或者其他命令。由于Mesos支持Docker,因此在本例中將使用可執(zhí)行的Docker鏡像。Docker是這樣一種技術(shù),它允許你將應(yīng)用程序和它運(yùn)行時(shí)需要的依賴(lài)一起打包。

為了在Mesos中使用Docker鏡像,這里需要在Docker registry中注冊(cè)它們的名稱(chēng):

const (
    MinerServerDockerImage = "derekchiang/p2pool"
    MinerDaemonDockerImage = "derekchiang/cpuminer"
)

然后定義一個(gè)常量,指定每個(gè)任務(wù)所需資源:

const (
    MemPerDaemonTask = 128  // mining shouldn't be memory-intensive
    MemPerServerTask = 256
    CPUPerServerTask = 1    // a miner server does not use much CPU
)

現(xiàn)在定義一個(gè)真正的scheduler,對(duì)其跟蹤,并確保其正確運(yùn)行需要的狀態(tài):

type MinerScheduler struct {
    // bitcoind RPC credentials
    bitcoindAddr string
    rpcUser      string
    rpcPass      string
    // mutable state
    minerServerRunning  bool
    minerServerHostname string 
    minerServerPort     int    // the port that miner daemons 
                               // connect to
    // unique task ids
    tasksLaunched        int
    currentDaemonTaskIDs []*mesos.TaskID
}

這個(gè)scheduler必須實(shí)現(xiàn)下面的接口:

type Scheduler interface {
    Registered(SchedulerDriver, *mesos.FrameworkID, *mesos.MasterInfo)
    Reregistered(SchedulerDriver, *mesos.MasterInfo)
    Disconnected(SchedulerDriver)
    ResourceOffers(SchedulerDriver, []*mesos.Offer)
    OfferRescinded(SchedulerDriver, *mesos.OfferID)
    StatusUpdate(SchedulerDriver, *mesos.TaskStatus)
    FrameworkMessage(SchedulerDriver, *mesos.ExecutorID, 
                     *mesos.SlaveID, string)
    SlaveLost(SchedulerDriver, *mesos.SlaveID)
    ExecutorLost(SchedulerDriver, *mesos.ExecutorID, *mesos.SlaveID, 
                 int)
    Error(SchedulerDriver, string)
}

現(xiàn)在一起看一個(gè)回調(diào)函數(shù):

func (s *MinerScheduler) Registered(_ sched.SchedulerDriver, 
      frameworkId *mesos.FrameworkID, masterInfo *mesos.MasterInfo) {
    log.Infoln("Framework registered with Master ", masterInfo)
}
func (s *MinerScheduler) Reregistered(_ sched.SchedulerDriver, 
      masterInfo *mesos.MasterInfo) {
    log.Infoln("Framework Re-Registered with Master ", masterInfo)
}
func (s *MinerScheduler) Disconnected(sched.SchedulerDriver) {
    log.Infoln("Framework disconnected with Master")
}

Registered在scheduler 成功向Mesos master注冊(cè)之后被調(diào)用。

Reregistered在scheduler 與Mesos master斷開(kāi)連接并且再次注冊(cè)時(shí)被調(diào)用,例如,在master重啟的時(shí)候。

Disconnected在scheduler 與Mesos master斷開(kāi)連接時(shí)被調(diào)用。這個(gè)在master掛了的時(shí)候會(huì)發(fā)生。

目前為止,這里僅僅在回調(diào)函數(shù)中打印了日志信息,因?yàn)閷?duì)于一個(gè)像這樣的簡(jiǎn)單框架,大多數(shù)回調(diào)函數(shù)可以空在那里。然而,下一個(gè)回調(diào)函數(shù)就是每一個(gè)框架的核心,必須要認(rèn)真的編寫(xiě)。

ResourceOffers在scheduler 從master那里得到一個(gè)offer的時(shí)候被調(diào)用。每一個(gè)offer包含一個(gè)集群上可以給框架使用的資源列表。資源通常包括CPU、內(nèi)存、端口和磁盤(pán)。一個(gè)框架可以使用它提供的一些資源、所有資源或者一點(diǎn)資源都不給用。

針對(duì)每一個(gè)offer,現(xiàn)在期望聚集所有的提供的資源并決定是否需要發(fā)布一個(gè)新的server任務(wù)或者一個(gè)新的worker任務(wù)。這里可以向每個(gè)offer發(fā)送盡可能多的任務(wù)以測(cè)試最大容量,但是由于開(kāi)采比特幣是依賴(lài)CPU的,所以這里每個(gè)offer運(yùn)行一個(gè)開(kāi)采者任務(wù)并使用所有可用的CPU資源。

for i, offer := range offers {
    // … Gather resource being offered and do setup
    if !s.minerServerRunning && mems >= MemPerServerTask &&
            cpus >= CPUPerServerTask && ports >= 2 {
        // … Launch a server task since no server is running and we 
        // have resources to launch it.
    } else if s.minerServerRunning && mems >= MemPerDaemonTask {
        // … Launch a miner since a server is running and we have mem 
        // to launch one.
    }
}

針對(duì)每個(gè)任務(wù)都需要?jiǎng)?chuàng)建一個(gè)對(duì)應(yīng)的TaskInfo message ,它包含了運(yùn)行這個(gè)任務(wù)需要的信息。

s.tasksLaunched++
taskID = &mesos.TaskID {
    Value: proto.String("miner-server-" + 
                        strconv.Itoa(s.tasksLaunched)),
}

Task IDs由框架決定,并且每個(gè)框架必須是唯一的。

containerType := mesos.ContainerInfo_DOCKER
task = &mesos.TaskInfo {
    Name: proto.String("task-" + taskID.GetValue()),
    TaskId: taskID,
    SlaveId: offer.SlaveId,
    Container: &mesos.ContainerInfo {
        Type: &containerType,
        Docker: &mesos.ContainerInfo_DockerInfo {
            Image: proto.String(MinerServerDockerImage),
        },
    },
    Command: &mesos.CommandInfo {
        Shell: proto.Bool(false),
        Arguments: []string {
            // these arguments will be passed to run_p2pool.py
            "--bitcoind-address", s.bitcoindAddr,
            "--p2pool-port", strconv.Itoa(int(p2poolPort)),
            "-w", strconv.Itoa(int(workerPort)),
            s.rpcUser, s.rpcPass,
        },
    },
    Resources: []*mesos.Resource {
        util.NewScalarResource("cpus", CPUPerServerTask),
        util.NewScalarResource("mem", MemPerServerTask),
    },
}

TaskInfo message指定了一些關(guān)于任務(wù)的重要元數(shù)據(jù)信息,它允許Mesos節(jié)點(diǎn)運(yùn)行Docker容器,特別會(huì)指定name、task ID、container information以及一些需要給容器傳遞的參數(shù)。這里也會(huì)指定任務(wù)需要的資源。

現(xiàn)在TaskInfo已經(jīng)被構(gòu)建好,因此任務(wù)可以這樣運(yùn)行:

driver.LaunchTasks([]*mesos.OfferID{offer.Id}, tasks, &mesos.Filters{RefuseSeconds: proto.Float64(1)})

在框架中,需要處理的最后一件事情是當(dāng)開(kāi)采者server關(guān)閉時(shí)會(huì)發(fā)生什么。這里可以利用StatusUpdate 函數(shù)來(lái)處理。

在一個(gè)任務(wù)的生命周期中,針對(duì)不同的階段有不同類(lèi)型的狀態(tài)更新。對(duì)這個(gè)框架來(lái)說(shuō),想要確保的是如果開(kāi)采者server由于某種原因失敗,系統(tǒng)會(huì)Kill所有開(kāi)采者worker以避免浪費(fèi)資源。這里是相關(guān)的代碼:

if strings.Contains(status.GetTaskId().GetValue(), "server") &&
    (status.GetState() == mesos.TaskState_TASK_LOST ||
        status.GetState() == mesos.TaskState_TASK_KILLED ||
        status.GetState() == mesos.TaskState_TASK_FINISHED ||
        status.GetState() == mesos.TaskState_TASK_ERROR ||
        status.GetState() == mesos.TaskState_TASK_FAILED) {
    s.minerServerRunning = false
    // kill all tasks
    for _, taskID := range s.currentDaemonTaskIDs {
        _, err := driver.KillTask(taskID)
        if err != nil {
            log.Errorf("Failed to kill task %s", taskID)
        }
    }
    s.currentDaemonTaskIDs = make([]*mesos.TaskID, 0)
}

萬(wàn)事大吉!通過(guò)努力,這里在Apache Mesos上建立一個(gè)正常工作的分布式比特幣開(kāi)采框架,它只用了大約300行GO代碼。這證明了使用Mesos 框架的API編寫(xiě)分布式系統(tǒng)是多么快速和簡(jiǎn)單。

看完上述內(nèi)容,你們對(duì)如何通過(guò)Mesos、Docker和Go創(chuàng)建一個(gè)分布式系統(tǒng)有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI