Docker与Golang的巧妙结合
【编者的话】这是一个展示在使用Go语言时如何让Docker更有用的提示与技巧的简辑。例如,如何使用不同版本的Go工具链来编译Go代码,如何交叉编译到不同的平台(并且测试结果!),或者如何制作真正小的容器镜像。
下面的文章假定你已经安装了Docker。不必是最新版本(这篇文章不会使用Docker任何花哨的功能)。
没有go的Go...意思是:“不用安装go就能使用Go”
如果你写Go代码,或者你对Go语言有一点点兴趣,你肯定要安装了Go编译器和Go工具链,所以你可能想知道:“重点是什么?”;但有些情况下,你想不安装Go就来编译Go。
机器上依旧有老版本Go 1.2(你不能或不想更新),不得不使用这个代码库,需要一个高版本的工具链。 想使用Go1.5的交叉编译功能(例如,确保能从一个Linux系统创建操作系统X的二进制文件)。 想拥有多版本的Go,但不想完全弄乱系统。 想100%确定项目和它所有的依赖,下载,建立和运行在一个纯净的系统上。如果遇到上述情况,找Docker来解决!
在容器里编译一个程序
当你安装了Go,你可以执行go get -v github.com/user/repo来下载,创建和安装一个库。(-v只是信息显示,如果你喜欢工具链快速和静默地运行,可以将它移除!)
你也可以执行go get github.com/user/repo/...来下载,创建和安装那个repo(包括库和二进制文件)里面所有的东西。
我们可以在一个容器里面这样做!
试试这个:
docker run golang go get -v github.com/golang/example/hello/...
这将拉取golang镜像(除非你已经有了,那它会马上启动),并且创建一个基于它的容器。在那个容器里,go会下载一个“hello world”的例子,创建它,安装它。但它会把它安装到这个容器里……我们现在怎么运行那个程序呢?
在容器里运行程序
一个办法是提交我们刚刚创建的容器,即,打包它到一个新的镜像:
docker commit $(docker ps -lq) awesomeness
注意:docker ps lq输出最后一个执行的容器的ID(只有ID!)。如果你是机器的唯一用户,并且你从上一个命令开始没有创建另一个容器,那这个容器就是你刚刚创建的“hello world”的例子。
现在,可以用刚刚构建的镜像创建容器来运行程序:
docker run awesomeness hello
输出会是Hello, Go examples!。
闪光点
当用docker commit构建镜像时,可以用--change标识指定任意Dockerfile命令。例如,可以使用一个CMD或者ENTRYPOINT命令以便docker run awesomeness自动执行hello。
在一次性容器上运行
如果不想创建额外的镜像只想运行这个Go程序呢?
使用:
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。新闻热点
疑难解答