如何通过代码实现Docker Client SDK自适应匹配所有服务端版本?
问题
我有一个通过Docker SDK管理容器的项目,使用WithAPIVersionNegotiation()选项来进行版本降级,示例代码如下:
cli, err := client.NewClientWithOpts(client.WithAPIVersionNegotiation()) ctx := context.Background() if err != nil { panic(err) } defer cli.Close() cmdStatementExecuteScript := cmdStatement optionsCreateExecuteScript := types.ExecConfig{ AttachStdout: true, AttachStderr: true, Cmd: cmdStatementExecuteScript, } response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) if err != nil { panic(err) }
但将服务部署到多台机器时,由于各机器的Docker版本不同,出现了如下错误:
panic: Error response from daemon: client is newer than server (client API version: 1.24, server API version: 1.22)
发现WithAPIVersionNegotiation配置并未降级到所需版本。不过当出现“Error response from daemon: client version 1.42 is too new. Maximum supported API version is 1.39”这类错误时,该配置是生效的。
请问是否有办法通过代码实现Docker Client SDK自适应适配所有服务端版本?
附go.mod依赖:
github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v23.0.4+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect
解决方案
1. 手动获取服务端版本并适配
绕过自动协商的局限性,先主动获取服务端的API版本,再创建对应版本的客户端:
ctx := context.Background() // 先建立临时连接获取服务端版本信息 tempCli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { panic(err) } serverInfo, err := tempCli.ServerVersion(ctx) if err != nil { panic(err) } tempCli.Close() // 用服务端API版本创建适配的客户端 serverAPIVersion := fmt.Sprintf("1.%d", serverInfo.APIVersion) cli, err := client.NewClientWithOpts( client.FromEnv, client.WithVersion(serverAPIVersion), ) if err != nil { panic(err) } defer cli.Close() // 后续使用cli执行容器操作 cmdStatementExecuteScript := cmdStatement optionsCreateExecuteScript := types.ExecConfig{ AttachStdout: true, AttachStderr: true, Cmd: cmdStatementExecuteScript, } response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) if err != nil { panic(err) }
2. 捕获版本错误并重试适配
针对自动协商未处理的版本不兼容错误,捕获后从错误信息中提取服务端版本,重新创建客户端:
import "regexp" import "strings" func getAdaptiveDockerClient(ctx context.Context) (*client.Client, error) { // 先尝试自动协商 cli, err := client.NewClientWithOpts(client.WithAPIVersionNegotiation()) if err == nil { return cli, nil } // 检查是否是客户端版本过高的错误 if apiErr, ok := err.(*errors.APIError); ok { if strings.Contains(apiErr.Message, "client is newer than server") { // 从错误信息中提取服务端API版本 matches := regexp.MustCompile(`server API version: (\d+\.\d+)`).FindStringSubmatch(apiErr.Message) if len(matches) == 2 { return client.NewClientWithOpts(client.WithVersion(matches[1])) } } } return nil, err } // 使用示例 ctx := context.Background() cli, err := getAdaptiveDockerClient(ctx) if err != nil { panic(err) } defer cli.Close() // 后续执行容器操作...
3. 降级SDK依赖版本
当前使用的github.com/docker/docker v23.0.4+incompatible对应较高的客户端API版本,对旧版Docker daemon兼容性较差。可以考虑将SDK降级到v20.10.x系列,该版本的自动协商逻辑对旧版服务端更友好,支持的API版本范围更广。
内容的提问来源于stack exchange,提问作者moluzhui
相关产品推荐
相关产品推荐

