如何用docker编译代码并且启动sever以及避免坑(上)

docker是很强大的容器,目前很多公司都在使用,今天简单说一下go的项目中启动docker,编译代码以及其中的一些坑。后续会有更多的模式和算法以及区块链相关的,如果你是想学习go语言或者是对设计模式或者算法感兴趣亦或是区块链开发工作者,都可以关注一下。(微信公众号:Go语言之美,csdn:Go语言之美。更多go语言知识信息等)。公众号会持续为大家分享更多干货。

如何用docker编译代码并且启动sever以及避免坑(上)

在我们安装docker之后,我们需要下载docker的sdk。附上docker sdk链接:

https://docs.docker.com/develop/sdk/examples/

我们可以调用docker的api来创建一个容器,同时编译代码,然后我们将编译好的文件可以再通过一个容器来作为我们的sever。这个使用场景是很常见的,每一个使用docker或者学习docker的人都应该掌握。

如果我们将编译代码部分做成自动化那么我们就可以将写好的代码发送到指定的程序中,程序里面可通过使用docker将代码编译成文件,再启动这个文件作为sever。

首先说创建容器时编译代码,下面的代码是官方文档上的示例代码创建容器:

package main
import (
	"io"
	"os"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"golang.org/x/net/context"
)
func main() {
	ctx := context.Background()
	cli, err := client.NewEnvClient()
	if err != nil {
		panic(err)
	}
	reader, err := cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
	if err != nil {
		panic(err)
	}
	io.Copy(os.Stdout, reader)
	resp, err := cli.ContainerCreate(ctx, &container.Config{
		Image: "alpine",
		Cmd: []string{"echo", "hello world"},
		Tty: true,
	}, nil, nil, "")
	if err != nil {
		panic(err)
	}
	if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
		panic(err)
	}
	statusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)
	select {
	case err := <-errCh:
		if err != nil {
			panic(err)
		}
	case <-statusCh:
	}
	out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
	if err != nil {
		panic(err)
	}
	io.Copy(os.Stdout, out)
}

在启动容器是我们可以执行一些命令,如果我们要编译代码就需要执行 go install 命令(go语言示例),同时我们再指定对应的GOPATH等需要的命令,这样在启动时就会执行这个命令,当然,要想使用go的命令首先要有golang的image,我们可以使用代码pull下来,同样可以提前手动pull下来。接下来我们要把准备编译的代码mount到容器中,这些在创建容器是都需要通过参数传进去。同时我们可以让这个容器等待我们编译代码完成再退出,同时我们也可以让这个容器自动销毁掉,因为在编译好代码后我们基本不会需要这个容器了。

这样问题就出现了,我们如何知道代码是否编译成功?幸好docker有查找logs的接口,在我们启动容器之后,我们可以通过查看他的日志,来判断go install命令是否成功执行。首先我们知道go install命令成功执行后是不会有任何输出的(没有消息就是好消息哈哈哈),我们可以通过判断这个容器是否产生日志来判断是否编译成功。这个方法安全吗?

可以告诉你,不安全,因为在查看logs的接口中,不会返回所有的日志信息,而且有可能即使产生日志,我们也可能通过这个接口查不到。原因大家可能也想到了,因为如果容器中一直产生日志,logs接口不可能将所有的日志到返回,所以基本上都是抓到第一屏的日志。所以这个方法不可以!

接下来我们换一个方法,go install的执行结果是在bin目录下产生对应的文件(bin目录是指GOPATH下的bin,如果没有这个目录会自动创建,如果有多个GOPATH,则会找第一个GOPATH,我们也可以设置GOBIN目录指定将文件生成到哪里),我们可不可以通过判断这个文件是否产生来判断呢?答案同样是不可以,第一,如果文件没有产生,即编译失败,我们拿不到具体的失败原因。第二,当我们去判断文件是否存在时,很可能编译成功,但是文件还未产生。

接下来说我得解决办法。我的思路是创建容器时执行一个脚本,脚本中执行go install,并且将结果重定向到指定文件,然后我们通过读取文件判断是否成功和对应的错误信息。脚本示例如下:

const goInstallShell = `#!/bin/sh
a=$(go install main 2>&1)
if [[ $? -eq 0 ]]; then
 echo "success" >> log
else
 echo "fail:" >> log
fi
echo ${a} >> log
`

这是docker的基本使用方法,下次继续写如果将这个可执行文件使用docker作为一个sever,感兴趣的可以点个赞,关注下,谢谢!公众号会持续为大家分享更多干货。

后续会有更多的模式和算法以及区块链相关的,如果你是想学习go语言或者是对设计模式或者算法感兴趣亦或是区块链开发工作者,都可以关注一下。(微信公众号:Go语言之美,csdn:Go语言之美。更多go语言知识信息等)。