实际上,Google数据中心里运行的Linux系统和CoreOS有很多相似之处。我记得2010年我刚离开Google加入腾讯的时候,一位腾讯的同事好奇地问:“Google的机群里用的Linux用什么软件包管理程序?是apt-get吗?还是yum?”我回答:“其实服务器上运行的Linux是不需要包管理的,只有桌面Linux系统才需要”。这位同事很难相信。其实,要不是因为“见了一回猪跑”,我也想不到会是这样。
CoreOS和其他Linux发行版本相比,执行效率高、内存耗费省;此外,利用双磁盘分区技术,即便是更新Linux内核也不需要重启。CoreOS还有很多独特之处,使得它在问世后很短的时间里就被Amazon和Google采用。如果想进一步了解这些特性,请看这个对Docker作者的访谈。
Go语言接下来,我们看看���何在Mac上用Go语言写一个极简化的搜索引擎,并且封装成集装箱镜像。
我们选择Go语言为例,而不是更常见的Java、Python、Perl、Ruby、Scala等,有很现实的原因——后面这些语言写的程序,在执行时都需要某些运行环境的支持。比如,Java程序依赖Java虚拟机,Python程序需要Python解释器,这些加上预装的程序库需要占用几百MB的集装箱空间。而用Go写的程序默认是全静态编译的,执行时不需要任何环境支持,不需要预装库,甚至连Linux系统动态库都不依赖。鉴于一家公司的系统往往由成千上万的集装箱构成;每个集装箱少几百MB,能为公司省出很大一笔开销。那些每月要向Amazon或者Goolgle付账的公司,对此必然印象深刻。这是Go语言在很多创业公司拓展迅猛的一个原因。
如果我们用C或者C++开发,也可以生成全静态链接的二进制程序文件。但是在Web时代,C/C++的开发效率不如Go。Google里倒是普遍使用C++,但是Google里有一套精心设计、积攒多年的C++库,这是外界没有的。外界普遍得使用第三方库,并往往因此挠头。比如,不同的第三方库(Thrift和boost)各有各的线程池机制,很难统一管理多线程。C++11倒是有了标准线程管理,但是把很多库统一到C++11是一项开销极大的工作。Go语言是专门为分布式系统开发设计的,根本就没有线程的概念,在语法上用goroutine代替了,线程池实现在Go runtime里,被编译进每个二进制程序。
交叉编译因为集装箱用主机的操作系统和硬件来执行程序,而Docker只支持Linux,所以Go程序必须被编译成Linux二进制文件,才能通过Docker运行。而我们在Mac上开发,需要利用交叉编译技术来生成Linux二进制文件。
为了得到一个支持交叉编译的Go语言编译器,我们需要从源码安装Go,并且需要做一些额外的安装工作。具体过程如下:
1. 安装Xcode,从而获得C编译器。
2. 下载Go编译器的源码包。比如Go 1.3在这里。
3. 解压和编译
tar xzvf go1.3.src.tar.gzcd go/src
./all.bash
4. 编译各种平台下的Go标准库
git clone git://github.com/davecheney/golang-crosscompile.gitsource golang-crosscompile/crosscompile.bash
go-crosscompile-build-all
这里,我们用到了Dave Cheney写的一个Bash脚本程序。这个程序支持生成以下平台上的Go语言标准库:
darwin/386
darwin/amd64
freebsd/386
freebsd/amd64
freebsd/arm
linux/386
linux/amd64
linux/arm
windows/386
windows/amd64
nacl/amd64
nacl/386
并行计算最常用的目标平台是linux/amd64——64bit的Linux系统,也是CoreOS的平台格式。下文中我们会演示如何在Mac下用这个编译器生成Linux平台的二进制代码文件。