摘要:在分布式系統用例上,比特幣開采就是一個很好的例子。會和一個比特幣采礦池通信,并給每個分配。這里是相關的代碼萬事大吉通過努力,這里在上建立一個正常工作的分布式比特幣開采框架,它只用了大約行代碼。
【摘要】雖然 Docker 和 Mesos 已成為不折不扣的 Buzzwords ,但是對于大部分人來說它們仍然是陌生的,下面我們就一起領略 Mesos 、Docker 和 Go 配合帶來的強大破壞力,如何通過 300 行代碼打造一個比特幣開采系統。
時下,對于大部分 IT 玩家來說, Docker 和 Mesos 都是熟悉和陌生的:熟悉在于這兩個詞無疑已成為大家討論的焦點,而陌生在于這兩個技術并未在生產環境得到廣泛使用,因此很多人仍然不知道它們究竟有什么優勢,或者能干什么。近日, John Walter 在 Dzone 上撰文 Creating a Distributed System in 300 Lines With Mesos, Docker, and Go,講述了 Mesos、Docker 和 Go 配合帶來的強大破壞力,本文由 OneAPM 工程師編譯整理。
誠然,構建一個分布式系統是很困難的,它需要可擴展性、容錯性、高可用性、一致性、可伸縮以及高效。為了達到這些目的,分布式系統需要很多復雜的組件以一種復雜的方式協同工作。例如,Apache Hadoop 在大型集群上并行處理 TB 級別的數據集時,需要依賴有著高容錯的文件系統( HDFS )來達到高吞吐量。
在之前,每一個新的分布式系統,例如 Hadoop 和 Cassandra ,都需要構建自己的底層架構,包括消息處理、存儲、網絡、容錯性和可伸縮性。慶幸的是,像 Apache Mesos 這樣的系統,通過給分布式系統的關鍵構建模塊提供類似操作系統的管理服務,簡化了構建和管理分布式系統的任務。Mesos 抽離了 CPU 、存儲和其它計算資源,因此開發者開發分布式應用程序時能夠將整個數據中心集群當做一臺巨型機對待。
構建在 Mesos 上的應用程序被稱為框架,它們能解決很多問題: Apache Spark,一種流行的集群式數據分析工具;Chronos ,一個類似 cron 的具有容錯性的分布式 scheduler ,這是兩個構建在 Mesos 上的框架的例子。構建框架可以使用多種語言,包括 C++,Go,Python,Java,Haskell 和 Scala。
在分布式系統用例上,比特幣開采就是一個很好的例子。比特幣將為生成 acceptable hash 的挑戰轉為驗證一塊事務的可靠性。可能需要幾十年,單臺筆記本電腦挖一塊可能需要花費超過 150 年。結果是,有許多的“采礦池”允許采礦者將他們的計算資源聯合起來以加快挖礦速度。Mesosphere 的一個實習生, Derek ,寫了一個比特幣開采框架(https://github.com/derekchiang/Mesos-Bitcoin-Miner),利用集群資源的優勢來做同樣的事情。在接下來的內容中,會以他的代碼為例。
1 個 Mesos 框架有 1 個 scheduler 和 1 個 executor 組成。scheduler 和 Mesos master 通信并決定運行什么任務,而 executor 運行在 slaves 上面,執行實際任務。大多數的框架實現了自己的 scheduler,并使用 1 個由 Mesos 提供的標準 executors 。當然,框架也可以自己定制 executor 。在這個例子中即會編寫定制的 scheduler,并使用標準命令執行器( executor )運行包含我們比特幣服務的 Docker 鏡像。
對這里的 scheduler 來說,需要運行的有兩種任務—— one miner server task and multiple miner worker tasks。 server 會和一個比特幣采礦池通信,并給每個 worker 分配 blocks 。Worker 會努力工作,即開采比特幣。
任務實際上被封裝在 executor 框架中,因此任務運行意味著告訴 Mesos master 在其中一個 slave 上面啟動一個 executor 。由于這里使用的是標準命令執行器(executor),因此可以指定任務是二進制可執行文件、bash 腳本或者其他命令。由于 Mesos 支持 Docker,因此在本例中將使用可執行的 Docker 鏡像。Docker 是這樣一種技術,它允許你將應用程序和它運行時需要的依賴一起打包。
為了在 Mesos 中使用 Docker 鏡像,這里需要在 Docker registry 中注冊它們的名稱:
const ( MinerServerDockerImage = "derekchiang/p2pool" MinerDaemonDockerImage = "derekchiang/cpuminer" )
然后定義一個常量,指定每個任務所需資源:
const ( MemPerDaemonTask = 128 // mining shouldn"t be memory-intensive MemPerServerTask = 256 CPUPerServerTask = 1 // a miner server does not use much CPU )
現在定義一個真正的 scheduler ,對其跟蹤,并確保其正確運行需要的狀態:
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 }
這個 scheduler 必須實現下面的接口:
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) }
現在一起看一個回調函數:
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 注冊之后被調用。
Reregistered 在 scheduler 與 Mesos master 斷開連接并且再次注冊時被調用,例如,在 master 重啟的時候。
Disconnected 在 scheduler 與 Mesos master 斷開連接時被調用。這個在 master 掛了的時候會發生。
目前為止,這里僅僅在回調函數中打印了日志信息,因為對于一個像這樣的簡單框架,大多數回調函數可以空在那里。然而,下一個回調函數就是每一個框架的核心,必須要認真的編寫。
ResourceOffers 在 scheduler 從 master 那里得到一個 offer 的時候被調用。每一個 offer 包含一個集群上可以給框架使用的資源列表。資源通常包括 CPU 、內存、端口和磁盤。一個框架可以使用它提供的一些資源、所有資源或者一點資源都不給用。
針對每一個 offer ,現在期望聚集所有的提供的資源并決定是否需要發布一個新的 server 任務或者一個新的 worker 任務。這里可以向每個 offer 發送盡可能多的任務以測試最大容量,但是由于開采比特幣是依賴 CPU 的,所以這里每個 offer 運行一個開采者任務并使用所有可用的 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. } }
針對每個任務都需要創建一個對應的 TaskInfo message ,它包含了運行這個任務需要的信息。
s.tasksLaunched++ taskID = &mesos.TaskID { Value: proto.String("miner-server-" + strconv.Itoa(s.tasksLaunched)), }
Task IDs 由框架決定,并且每個框架必須是唯一的。
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 指定了一些關于任務的重要元數據信息,它允許 Mesos 節點運行 Docker 容器,特別會指定 name、task ID、container information 以及一些需要給容器傳遞的參數。這里也會指定任務需要的資源。
現在 TaskInfo 已經被構建好,因此任務可以這樣運行:
driver.LaunchTasks([]*mesos.OfferID{offer.Id}, tasks, &mesos.Filters{RefuseSeconds: proto.Float64(1)})
在框架中,需要處理的最后一件事情是當開采者 server 關閉時會發生什么。這里可以利用 StatusUpdate 函數來處理。
在一個任務的生命周期中,針對不同的階段有不同類型的狀態更新。對這個框架來說,想要確保的是如果開采者 server 由于某種原因失敗,系統會 Kill 所有開采者 worker 以避免浪費資源。這里是相關的代碼:
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) }
萬事大吉!通過努力,這里在 Apache Mesos 上建立一個正常工作的分布式比特幣開采框架,它只用了大約 300 行 GO 代碼。這證明了使用 Mesos 框架的 API 編寫分布式系統是多么快速和簡單。
原文鏈接:Creating a Distributed System in 300 Lines With Mesos, Docker, and Go
本文由OneAPM工程師編譯 ,想閱讀更多技術文章,請訪問OneAPM官方技術博客。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/26433.html
摘要:負責承載操作系統的分布式文件系統只需要使用必要的文件,而且事實上只需要下載并在本地緩存這部分必要數據。而第二項原則在于元數據即與文件存在相關的信息,而非文件內容被優先對待。這套鏡像隨后可進行任意分發,并被用于啟動該項任務。 隨著Docker技術的日漸火熱,一些容器相關的問題也浮出水面。本文就容器數量激增后造成的分發效率低下問題進行了探討,并提出了一種新的解決方法。發現問題,解決問題,正...
摘要:今天小數給大家帶來一篇技術正能量滿滿的分享來自社區線上群分享的實錄,分享嘉賓是數人云肖德時。第二級調度由被稱作的組件組成。它們是最小的部署單元,由統一創建調度管理。 今天小數給大家帶來一篇技術正能量滿滿的分享——來自KVM社區線上群分享的實錄,分享嘉賓是數人云CTO肖德時。 嘉賓介紹: 肖德時,數人云CTO 十五年計算機行業從業經驗,曾為紅帽 Engineering Service ...
摘要:容器內文件日志平臺支持的文件存儲是,避免了許多復雜環境的處理。以上是數人云在實踐容器日志系統過程中遇到的問題,更高層次的應用包括容器日志分析等,還有待繼續挖掘和填坑,歡迎大家提出建議,一起交流。 業務平臺每天產生大量日志數據,為了實現數據分析,需要將生產服務器上的所有日志收集后進行大數據分析處理,Docker提供了日志驅動,然而并不能滿足不同場景需求,本次將結合實例分享日志采集、存儲以...
摘要:相關基于項目和項目,并遵循應用的十二因素風格。相關在設計上,項目盡量保持驅動和模塊化,以便模塊支持不同的實現方案。相關不僅可以管理眾多虛擬機,其計算服務還支持對的驅動,管理引擎的子項目還可用于通過模板管理容器。現已整合公司所支持的項目。 整理自《Docker技術入門與實踐》 PaaS(Platform as a Service) PaaS 是希望提供一個統一的可供所有軟件直接運行而無需...
摘要:由谷歌開發,允許你在許多不同的主機上管理容器化應用程序。它已經被完全開源,谷歌在年首次宣布開發它,第一版在夏天的時候發布。除了最近幾年的收獲,本身也是基于谷歌內部十多年使用容器技術的經驗。 基于云的基礎設施,容器,微服務和新編程平臺在世界范圍占據了一大塊媒體領域,橫掃IT界。Docker、容器的使用在這幾個月內呈爆炸式增長,已經提交了20億的鏡像pulls;鏡像數在2015年11月就已...
閱讀 1600·2021-09-23 11:21
閱讀 2357·2021-09-07 10:13
閱讀 843·2021-09-02 10:19
閱讀 1140·2019-08-30 15:44
閱讀 1732·2019-08-30 13:18
閱讀 1920·2019-08-30 11:15
閱讀 1115·2019-08-29 17:17
閱讀 2024·2019-08-29 15:31