正文
docker run --rm golang sh -c \
"go get github.com/golang/example/hello/... && exec hello"
等等,那些花哨的东西是什么?
-
--rm
告诉Docker CLI一旦容器退出,就自动发起一个
docker rm
命令。那样,不会留下任何东西。
-
使用shell逻辑运算符
&&
把创建步骤(
go get
)和执行步骤(
exec hello
)联接在一起。如果不喜欢shell,
&&
意思是“与”。它允许第一部分
go get...
,并且如果(而且仅仅是如果!)那部分运行成功,它将执行第二部分(
exec hello
)。如果你想知道为什么这样:它像一个懒惰的
and
计算器,只有当左边的值是
true
才计算右边的。
-
传递命令到
sh –c
,因为如果是简单的做
docker run golang "go get ... && hello"
,Docker将试着执行名为
go SPACE get SPACE etc
的程序。并且那不会起作用。因此,我们启动一个shell,并让shell执行命令序列。
-
使用
exec hello
而不是
hello
:这将使用hello程序替代当前的进程(我们刚才启动的shell)。这确保
hello
在容器里是PID 1。而不是shell的是PID 1而
hello
作为一个子进程。这对这个微小的例子毫无用处,但是当运行更有用的程序,这将允许它们正确地接收外部信号,因为外部信号是发送给容器里的PID 1。你可能会想,什么信号啊?好的例子是
docker stop
,发送
SIGTERM
给容器的PID 1。
使用不同版本的Go
当使用
golang
镜像,Docker扩展为
golang:latest,
将(像你所猜的)映射到Docker Hub上的最新可用版本。
如果想用一个特定的Go版本,很容易:在镜像名字后面用那个版本做标签指定它。
例如,想用Go 1.5,修改上面的例子,用
golang:1.5
替换
golang
:
docker run --rm golang:1.5 sh -c \
"go get github.com/golang/example/hello/... && exec hello"
你能在Docker Hub的Golang镜像页面上看到所有可用的版本(和变量)。
在系统上安装
好了,如果想在系统上运行编译好的程序,而不是一个容器呢?我们将复制这个编译了的二进制文件到容器外面。注意,仅当容器架构和主机架构匹配的时候,才会起作用;换言之,如果在Linux上运行Docker。(我排除的可能是运行Windows容器的人!)
最容易在容器外获得二进制文件的方法是映射
$GOPATH/bin
目录到一个本地目录,在
golang
容器里,
$GOPATH
是
/go.
所以我们可以如下操作:
docker run -v /tmp/bin:/go/bin \
golang go get github.com/golang/example/hello/...
/tmp/bin/hello
如果在Linux上,将看到
Hello, Go examples!
消息。但如果是,例如在Mac上,可能会看到:
-bash:
/tmp/test/hello: cannot execute binary file
我们又能做什么呢?
交叉编译
Go 1.5具备优秀的开箱即用交叉编译能力,所以如果你的容器操作系统和/或架构和你的系统不匹配,根本不是问题!
开启交叉编译,需要设置
GOOS
和/或
GOARCH
。
例如,假设在64位的Mac上:
docker run -e GOOS=darwin -e GOARCH=amd64 -v /tmp/crosstest:/go/bin \
golang go get github.com/golang/example/hello/...
交叉编译的输出不是直接在
$GOPATH/bin
,而是在
$GOPATH/bin/$GOOS_$GOARCH.
。换言之,想运行程序,得执行
/tmp/crosstest/darwin_amd64/hello.
。