Docker(Moby)golang镜像构建日志是Base64编码的。

huangapple go评论84阅读模式
英文:

Docker (Moby) golang image build logs are base64 encoded

问题

我正在寻找有关从使用Docker客户端库的基于Golang的客户端发送的dockerd(buildkit/moby)镜像构建请求中提取图像构建日志的帮助。

我可以成功请求图像构建,并接收到JSON消息的日志流,然后将其解码为Jsonmessage实例。但是,构建器的实际日志行似乎是在每个JSON消息的aux字段中进行了Base64编码

我可以轻松解码Base64,但它们似乎包含奇怪的终端控制字符和可能是错误编码的数据,这让我想知道它们是否实际上是某种我应该解包的结构的Base64编码。

让我困惑的是,当显示docker buildx build的构建进度时,我找不到docker-ce或moby代码中似乎对aux有效载荷进行Base64解码的任何内容。

据我所知,buildx代码对aux有效载荷没有进行任何特殊处理:https://github.com/docker/docker-ce/blob/523cf7e71252013fbb6a590be67a54b4a88c1dae/components/cli/cli/command/image/build_buildkit.go#L325

例如,简化的构建代码如下:

        image := Image{Name:      "test"}
        contextreader, err := archive.TarWithOptions(buildConf.Build.Context, &archive.TarOptions{})
        if err != nil {
                return err
        }
        imageBuildResponse, err := b.client.ImageBuild(
                ctx,
                contextreader,
                types.ImageBuildOptions{
                      Version:     types.BuilderBuildKit,
                      Context:     contextreader,
                      Dockerfile:  dockerfile,
                })
        if err != nil {
                return err
        }
        defer imageBuildResponse.Body.Close()


        buf := bytes.NewBuffer(nil)

        imageID := ""
        writeAux := func(msg jsonmessage.JSONMessage) {
                if msg.ID == "moby.image.id" {
                        var result types.BuildResult
                        if err := json.Unmarshal(*msg.Aux, &result); err != nil {
                            panic("don't do this in your real code")
                        }
                        imageID = result.ID
                        return
                }
                return err
        }

        err := jsonmessage.DisplayJSONMessagesStream(imageBuildResponse.Body, buf, os.Stderr.Fd(), false /* not terminal */, writeAux)
        if err != nil {
                if jerr, ok := err.(*jsonmessage.JSONError); ok {
                        // If no error code is set, default to 1
                        if jerr.Code == 0 {
                                jerr.Code = 1
                        }
                        return fmt.Errorf("error while building image: %s", jerr.Message)
                }
        }

将会像这样将JSON有效载荷写入stderr:

{"id":"moby.buildkit.trace","aux":"Cn0KR3NoYTI1NjozZThhMzMxYmRkZGFjNWZkYmNjOGVhMDFmYWFhYmM3MjA0MDkwMmYwNjdmYzRhOGY0NDJmMmIzYWVlN2RkNGIyGiRbaW50ZXJuYWxdIGxvYWQgcmVtb3RlIGJ1aWxkIGNvbnRleHQqDAiYw8KaBhCykpCqAg=="}
{"id":"moby.buildkit.trace","aux":"CokBCkdzaGEyNTY6M2U4YTMzMWJkZGRhYzVmZGJjYzhlYTAxZmFhYWJjNzIwNDA5MDJmMDY3ZmM0YThmNDQyZjJiM2FlZTdkZDRiMhokW2ludGVybmFsXSBsb2FkIHJlbW90ZSBidWlsZCBjb250ZXh0KgwImMPCmgYQspKQqgIyCgiZw8KaBhD08F0="}

这里的Base64字符串解码后不是有效的UTF-8,并且它们也不符合ISO-8859-1。例如,使用UTF-8控制台编码:

$ base64 -d <<< 'Cn0KR3NoYTI1NjozZThhMzMxYmRkZGFjNWZkYmNjOGVhMDFmYWFhYmM3MjA0MDkwMmYwNjdmYzRhOGY0NDJmMmIzYWVlN2RkNGIyGiRbaW50ZXJuYWxdIGxvYWQgcmVtb3RlIGJ1aWxkIGNvbnRleHQqDAiYw8KaBhCykpCqAg=='

}
Gsha256:3e8a331bdddac5fdbcc8ea01faaabc72040902f067fc4a8f442f2b3aee7dd4b2�$[internal] load remote build context*
                                                                                                              ������

看起来它可能是一个结构体,但是我无论如何都找不到解码和处理它的方法。

英文:

I'm looking for help with extracting the image build logs from a dockerd (buildkit/moby) image build request sent by a Golang based client using the docker client libraries.

I can request the image build fine and receive the log stream of json messages then decode them as Jsonmessage instances. But the actual log lines from the builder appear to be base64 encoded in an aux field of each json message.

I can decode the base64 easily enough, but they seem to include odd terminal control characters and possibly mis-encoded data, which makes me wonder if they're actually a base64 encoding of some kind of struct I'm supposed to unpack.

What confuses me is that I can't find anything in the docker-ce or moby code that seems to base64-decode an 'aux' payload when processing logs when displaying build progress for docker buildx build.

As far as I can tell, the buildx code doesn't do anything special to the aux payload: https://github.com/docker/docker-ce/blob/523cf7e71252013fbb6a590be67a54b4a88c1dae/components/cli/cli/command/image/build_buildkit.go#L325

For example, trimmed-down build code like:

        image := Image{Name:      &quot;test&quot;}
contextreader, err := archive.TarWithOptions(buildConf.Build.Context, &amp;archive.TarOptions{})
if err != nil {
return err
}
imageBuildResponse, err := b.client.ImageBuild(
ctx,
contextreader,
types.ImageBuildOptions{
Version:     types.BuilderBuildKit,
Context:     contextreader,
Dockerfile:  dockerfile,
})
if err != nil {
return err
}
defer imageBuildResponse.Body.Close()
buf := bytes.NewBuffer(nil)
imageID := &quot;&quot;
writeAux := func(msg jsonmessage.JSONMessage) {
if msg.ID == &quot;moby.image.id&quot; {
var result types.BuildResult
if err := json.Unmarshal(*msg.Aux, &amp;result); err != nil {
panic(&quot;don&#39;t do this in your real code&quot;)
}
imageID = result.ID
return
}
return err
}
err := jsonmessage.DisplayJSONMessagesStream(imageBuildResponse.Body, buf, os.Stderr.Fd(), false /* not terminal */, writeAux)
if err != nil {
if jerr, ok := err.(*jsonmessage.JSONError); ok {
// If no error code is set, default to 1
if jerr.Code == 0 {
jerr.Code = 1
}
return fmt.Errorf(&quot;error while building image: %s&quot;, jerr.Message)
}
}

will write json payloads to stderr like

{&quot;id&quot;:&quot;moby.buildkit.trace&quot;,&quot;aux&quot;:&quot;Cn0KR3NoYTI1NjozZThhMzMxYmRkZGFjNWZkYmNjOGVhMDFmYWFhYmM3MjA0MDkwMmYwNjdmYzRhOGY0NDJmMmIzYWVlN2RkNGIyGiRbaW50ZXJuYWxdIGxvYWQgcmVtb3RlIGJ1aWxkIGNvbnRleHQqDAiYw8KaBhCykpCqAg==&quot;}
{&quot;id&quot;:&quot;moby.buildkit.trace&quot;,&quot;aux&quot;:&quot;CokBCkdzaGEyNTY6M2U4YTMzMWJkZGRhYzVmZGJjYzhlYTAxZmFhYWJjNzIwNDA5MDJmMDY3ZmM0YThmNDQyZjJiM2FlZTdkZDRiMhokW2ludGVybmFsXSBsb2FkIHJlbW90ZSBidWlsZCBjb250ZXh0KgwImMPCmgYQspKQqgIyCgiZw8KaBhD08F0=&quot;}

The base64 strings here don't decode as valid utf-8, and they don't make sense as ISO-8859-1 either. E.g. with a utf-8 console encoding:

$ base64 -d &lt;&lt;&lt;&#39;Cn0KR3NoYTI1NjozZThhMzMxYmRkZGFjNWZkYmNjOGVhMDFmYWFhYmM3MjA0MDkwMmYwNjdmYzRhOGY0NDJmMmIzYWVlN2RkNGIyGiRbaW50ZXJuYWxdIGxvYWQgcmVtb3RlIGJ1aWxkIGNvbnRleHQqDAiYw8KaBhCykpCqAg==&#39;
}
Gsha256:3e8a331bdddac5fdbcc8ea01faaabc72040902f067fc4a8f442f2b3aee7dd4b2�$[internal] load remote build context*
������

It looks like it's probably a struct, but for the life of me I can't find what decodes and processes it.

答案1

得分: 1

当我写SO问题时,我当然找到了答案...

build_buildkit.go中的writeAux函数调用了一个tracer实例的write方法,这才是真正的工作。我一定是瞎了。

这些消息是来自github.com/moby/buildkit/api/services/control包中的StatusResponse的序列化实例。它们被解码为base64字节序列并进行检查。如果你只想要日志并跳过其他内容,只需查找具有非空Logs成员数组的实例,例如在上述的writeAux函数中的以下代码:

                } else if msg.ID == "moby.buildkit.trace" {
                        // Process the message like
                        // https://github.com/docker/docker-ce/blob/523cf7e71252013fbb6a590be67a54b4a88c1dae/components/cli/cli/command/image/build_buildkit.go#L386
                        // the 'tracer.write' method in build_buildkit.go
                        var resp controlapi.StatusResponse
                        var dt []byte
                        // ignoring all messages that are not understood
                        if err := json.Unmarshal(*msg.Aux, &dt); err != nil {
                                return
                        }
                        if err := (&resp).Unmarshal(dt); err != nil {
                                return
                        }
                        for _, v := range resp.Vertexes {
                                fmt.Printf("layer: %+v", v)
                        }
                        for _, v := range resp.Statuses {
                                fmt.Printf("status: %+v", v)
                        }
                        for _, v := range resp.Logs {
                                fmt.Printf("log: msg.Msg)
                        }
                  }

json.Unmarshalcontrolapi.StatusResponse.Unmarshal会为您进行base64解码和拆包。

英文:

So of course I find the answer while writing up the SO question...

The writeAux function in build_buildkit.go calls the write method of a tracer instance, and that does the real work. I must've been blind.

The messages are serialized instances of StatusResponse from the github.com/moby/buildkit/api/services/control package. They are unmarshalled from base64-decoded byte sequences and inspected. If you want logs and to skip everything else, just look for instances with non-empty Logs member arrays, e.g. something like this within the above writeAux function:

                } else if msg.ID == &quot;moby.buildkit.trace&quot; {
// Process the message like
// https://github.com/docker/docker-ce/blob/523cf7e71252013fbb6a590be67a54b4a88c1dae/components/cli/cli/command/image/build_buildkit.go#L386
// the &#39;tracer.write&#39; method in build_buildkit.go
var resp controlapi.StatusResponse
var dt []byte
// ignoring all messages that are not understood
if err := json.Unmarshal(*msg.Aux, &amp;dt); err != nil {
return
}
if err := (&amp;resp).Unmarshal(dt); err != nil {
return
}
for _, v := range resp.Vertexes {
fmt.Printf(&quot;layer: %+v&quot;, v)
}
for _, v := range resp.Statuses {
fmt.Printf(&quot;status: %+v&quot;, v)
}
for _, v := range resp.Logs {
fmt.Printf(&quot;log: msg.Msg)
}
}

The json.Unmarshal and controlapi.StatusResponse.Unmarshal do the base64 decoding and unpacking for you.

huangapple
  • 本文由 发表于 2022年10月20日 09:48:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/74133617.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定