介绍
docker
我正在尝试在 Linux VM 上使用以下版本(uname -a
返回Linux xen 4.1.17-yocto-standard #1 SMP PREEMPT Thu Jun 2 13:29:47 PDT 2016 x86_64 GNU/Linux)
,从docker_git
BitBake 配方。
如果我尝试运行docker version
,我会得到以下输出:
Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.3
Git commit (client): 7c8fca2-dirty
OS/Arch (client): linux/amd64
然后命令挂起。
它应该是什么样子
我尝试docker version
在可运行的 docker 安装(Ubuntu 14.04)上执行,并得到以下输出:
Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.2.1
Git commit (client): 7c8fca2
OS/Arch (client): linux/amd64
Server version: 1.6.2
Server API version: 1.18
Go version (server): go1.2.1
Git commit (server): 7c8fca2
OS/Arch (server): linux/amd64
因此,我假设在获取服务器信息时出现了某种错误。
进一步研究
我对 Go 不太熟悉,因此当我试图弄清楚这里到底发生了什么时,这部分内容可能会让我感到困惑。
我开始看这部分api/client/version.go
Docker 源代码:
var versionTemplate = `Client:
Version: {{.Client.Version}}
API version: {{.Client.APIVersion}}
Go version: {{.Client.GoVersion}}
Git commit: {{.Client.GitCommit}}
Built: {{.Client.BuildTime}}
OS/Arch: {{.Client.Os}}/{{.Client.Arch}}{{if .Client.Experimental}}
Experimental: {{.Client.Experimental}}{{end}}{{if .ServerOK}}
Server:
Version: {{.Server.Version}}
API version: {{.Server.APIVersion}}
Go version: {{.Server.GoVersion}}
Git commit: {{.Server.GitCommit}}
Built: {{.Server.BuildTime}}
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}{{if .Server.Experimental}}
Experimental: {{.Server.Experimental}}{{end}}{{end}}`
它继续到这一部分:
vd := types.VersionResponse{
Client: &types.Version{
Version: dockerversion.Version,
APIVersion: cli.client.ClientVersion(),
GoVersion: runtime.Version(),
GitCommit: dockerversion.GitCommit,
BuildTime: dockerversion.BuildTime,
Os: runtime.GOOS,
Arch: runtime.GOARCH,
Experimental: utils.ExperimentalBuild(),
},
}
// VersionResponse holds version information for the client and the server
type VersionResponse struct {
Client *Version
Server *Version
}
因此,此时需要做的就是为Server
成员(类型)分配一些值。这发生在上述分配*Version
之后的部分:vd
serverVersion, err := cli.client.ServerVersion(context.Background())
if err == nil {
vd.Server = &serverVersion
}
函数定义ServerVersion
如下engine-api/client/version.go
// ServerVersion returns information of the docker client and server host.
func (cli *Client) ServerVersion(ctx context.Context) (types.Version, error) {
resp, err := cli.get(ctx, "/version", nil, nil)
if err != nil {
return types.Version{}, err
}
var server types.Version
err = json.NewDecoder(resp.body).Decode(&server)
ensureReaderClosed(resp)
return server, err
}
据我所知,上述get
函数调用指向client/request.go
来自 Docker 的engine API
仓库
// getWithContext sends an http request to the docker API using the method GET with a specific go context.
func (cli *Client) get(ctx context.Context, path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
return cli.sendRequest(ctx, "GET", path, query, nil, headers)
}
在哪里:
ctx
是context.Background()
path
是/version
- 不
query
- 不
headers
并且这个文档sendRequest
来自vendor/src/google.golang.org/grpc/call.go
:
// sendRequest writes out various information of an RPC such as Context and Message.
func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
stream, err := t.NewStream(ctx, callHdr)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
if _, ok := err.(transport.ConnectionError); !ok {
t.CloseStream(stream, err)
}
}
}()
// TODO(zhaoq): Support compression.
outBuf, err := encode(codec, args, compressionNone)
if err != nil {
return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err)
}
err = t.Write(stream, outBuf, opts)
if err != nil {
return nil, err
}
// Sent successfully.
return stream, nil
}
这只是猜测了一段时间,现在我担心自己可能找错了地方。
问题
- 什么原因导致
docker version
、docker run hello-world
、docker images
、docker ps
和docker info
挂起以及如何修复? - 或者有没有更有效的方法来检查这个错误的原因?
答案1
您的strace
输出强烈表明 Docker 客户端无法与 Docker 守护进程通信,后者默认在 创建一个套接字/var/run/docker.sock
。
Docker 守护进程应该是一个系统服务(在 systemd 上,位于 ,/lib/systemd/system/docker.service
套接字配置为),但它可以使用后跟任何可选选项来/lib/systemd/system/docker.socket
独立启动。/usr/bin/docker daemon
你strace
应该守护进程而不是客户。
strace
在 Docker 守护进程上使用
获取 Docker 守护进程的进程 ID。这两个命令都会将 PID 存储在名为 的变量中
$DOCKER_PID
。直接从插座:
DOCKER_PID=$(sudo lsof -Ua /var/run/docker.sock | awk '/^docker/ {print $2}' | head -1)
系统:
DOCKER_PID=$(systemctl show -p MainPID docker.service | awk -F'=' '{print $NF}')
其他:
DOCKER_PID=$(ps aux | grep 'docker daemon' | grep -v 'grep' | awk '{print $2}' | head -1)
strace
现在您有了 PID,可以在 Docker 守护进程上使用:sudo strace -vvvfts1000 -p $DOCKER_PID
在单独的终端中,运行通常挂在 Docker 客户端中的命令。
docker version
观察
strace
Docker 守护进程来见证从套接字监听端开始发生的情况。
解释strace
输出
以下是运行时守护进程应该执行的操作docker version
:
阅读客户发送的内容:
[pid 14291] 12:34:36 <... read resumed> "GET /v1.22/version HTTP/1.1\r\nHost: \r\nUser-Agent: Docker-Client/1.10.3 (linux)\r\n\r\n", 4096) = 81
收集有关系统的信息:
[pid 14291] 12:34:36 uname({sysname="Linux", nodename="node51", release="4.4.0-22-generic", version="#40-Ubuntu SMP Thu May 12 22:03:46 UTC 2016", machine="x86_64", domainname="(none)"}) = 0
回复客户端有关系统的信息:
[pid 14291] 12:34:36 write(3, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nServer: Docker/1.10.3 (linux)\r\nDate: Mon, 13 Jun 2016 17:34:36 GMT\r\nContent-Length: 194\r\n\r\n{\"Version\":\"1.10.3\",\"ApiVersion\":\"1.22\",\"GitCommit\":\"20f81dd\",\"GoVersion\":\"go1.6.1\",\"Os\":\"linux\",\"Arch\":\"amd64\",\"KernelVersion\":\"4.4.0-22-generic\",\"BuildTime\":\"Wed, 20 Apr 2016 14:19:16 -0700\"}\n", 334) = 334
然后客户端(
docker version
)显示服务器返回的信息:Server: Version: 1.10.3 API version: 1.22 Go version: go1.6.1 Git commit: 20f81dd Built: Wed, 20 Apr 2016 14:19:16 -0700 OS/Arch: linux/amd64
在您的问题中,您的 Docker 守护进程显然没有执行步骤#3,因为如果它执行了,客户端就会看到回复,但客户端没有收到任何东西。
您应该能够使用此信息来找出 Docker 守护进程未回复客户端请求的原因。
可能的原因
您提供的信息不足以查明 Docker 客户端无法从 Docker 守护程序获取回复的原因,但这里有一些提示:
- Docker 守护进程是否正在运行?
- 如果在前台启动 Docker 守护进程会发生什么?:
sudo docker daemon
- Docker 守护进程是否正在监听 处的套接字
/var/run/docker.sock
? 应该在某处sudo lsof -p $DOCKER_PID
显示“ ”。/var/run/docker.sock type=STREAM
- 是否存在可以阻止客户端或守护进程中的某些内容的安全策略?在 Linux 上,SELinux 和 AppArmor 可能会引起混淆,因为为它们设置的策略可能会拒绝访问。
- 在守护进程中
strace
,如果您没有从客户端收到 HTTP GET 请求,则意味着服务器没有从套接字接收到任何内容。 - 如果您这样做了
docker version
并在守护进程中看到strace
没有uname()
调用,则守护进程甚至没有尝试获取有关系统的信息。 write()
如果在守护进程中看到调用strace
,则意味着守护进程已经回复,但是客户端没有看到它。- 这可能是您正在使用的旧版 Docker 中的一个已知问题。请尝试升级。