versão do docker trava antes da informação do servidor

3

Introdução

Estou tentando usar a seguinte versão do docker em uma VM do Linux ( uname -a retorna Linux xen 4.1.17-yocto-standard #1 SMP PREEMPT Thu Jun 2 13:29:47 PDT 2016 x86_64 GNU/Linux) , criada a partir do receita de docker_git BitBake .

Se eu tentar executar docker version , recebo a seguinte saída:

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

Então o comando trava.

Como deveria ser

Eu tentei executar docker version em uma instalação do docker de trabalho (Ubuntu 14.04) e recebo a seguinte saída:

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

Então, suponho que haja algum tipo de erro ao buscar informações do servidor.

Pesquisa adicional

Eu não estou familiarizado com o Go, então esta seção pode ser desconfortável enquanto tento descobrir o que diabos está acontecendo aqui.

Comecei a analisar esta parte do api/client/version.go do Docker código fonte:

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}}'

continua nesta seção:

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(),
    },
}

Em engine-api/types/client.go :

// VersionResponse holds version information for the client and the server
type VersionResponse struct {
    Client *Version
    Server *Version
} 

Portanto, tudo o que precisa ser feito neste momento é atribuir algo ao Server member (do tipo *Version ). Isso acontece na seção após a designação vd acima:

serverVersion, err := cli.client.ServerVersion(context.Background())
if err == nil {
    vd.Server = &serverVersion
}

A definição de função para ServerVersion é a seguinte de 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
}

Pelo que posso encontrar, a chamada acima da função get aponta para client/request.go do engine API repo do Docker

// 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)
}

Onde:

  • ctx é context.Background()
  • path é /version
  • não query
  • não headers

E esta documentação para sendRequest de 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
}

Isso tem sido adivinhação por um tempo e agora estou preocupado que eu possa estar procurando no lugar errado.

Perguntas

  • O que faz com que docker version , docker run hello-world , docker images , docker ps e docker info sejam interrompidos e como isso pode ser corrigido?
  • Ou há uma maneira mais eficiente de inspecionar a causa desse erro?
por karobar 24.05.2016 / 17:40

1 resposta

3

Sua saída strace sugere strongmente que o cliente do Docker não pode falar com o daemon do Docker, que por padrão cria um soquete em /var/run/docker.sock .

O daemon do Docker deve ser um serviço do sistema (no systemd, localizado em /lib/systemd/system/docker.service com a configuração de soquete em /lib/systemd/system/docker.socket ), mas pode ser iniciado independentemente usando /usr/bin/docker daemon seguido por quaisquer opções opcionais.

Você deve strace do daemon em vez do cliente .

Usando strace no daemon do Docker

  1. Obtenha o ID do processo do seu daemon do Docker. Qualquer um desses comandos armazenaria o PID em uma variável chamada $DOCKER_PID .

    • Diretamente do soquete:

      DOCKER_PID=$(sudo lsof -Ua /var/run/docker.sock | awk '/^docker/ {print $2}' | head -1)
      
    • systemd:

      DOCKER_PID=$(systemctl show -p MainPID docker.service | awk -F'=' '{print $NF}')
      
    • Outro:

      DOCKER_PID=$(ps aux | grep 'docker daemon' | grep -v 'grep' | awk '{print $2}' | head -1)
      
  2. Use strace no daemon do Docker, agora que você tem o PID:

    sudo strace -vvvfts1000 -p $DOCKER_PID
    
  3. Em um terminal separado, execute um comando que normalmente é interrompido no cliente do Docker.

    docker version
    
  4. Assista o strace no daemon do Docker para testemunhar o que acontece a partir da extremidade de escuta do soquete.

Interpretando a strace output

Veja o que o daemon deve fazer quando você executa docker version :

  1. Leia o que o cliente enviou:

    [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
    
  2. Colete informações sobre o sistema:

    [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
    
  3. Responder ao cliente com informações sobre o sistema:

    [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
    
  4. O cliente ( docker version ) exibe as informações retornadas pelo servidor:

    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
    

Em seu problema, seu daemon do Docker aparentemente não executou a etapa 3 porque, se tivesse, o cliente teria visto a resposta, mas o cliente não recebeu nada.

Você deve ser capaz de usar essas informações para descobrir por que o daemon do Docker não está respondendo às solicitações do cliente.

Causas possíveis

As informações que você forneceu não são suficientes para identificar a causa da incapacidade do seu cliente Docker de obter uma resposta do daemon do Docker, mas aqui estão algumas dicas:

  • O daemon do Docker está em execução?
  • O que acontece se você iniciar o daemon do Docker em primeiro plano ?: sudo docker daemon
  • O daemon do Docker está ouvindo o soquete em /var/run/docker.sock ? sudo lsof -p $DOCKER_PID deve mostrar " /var/run/docker.sock type=STREAM " em algum lugar.
  • Existem políticas de segurança que bloqueiam algo no cliente ou daemon? No Linux, o SELinux e o AppArmor podem causar confusão, pois as políticas definidas para eles podem negar o acesso.
  • No strace do daemon, se você não obtiver uma solicitação HTTP GET do cliente, isso significa que o servidor não recebeu nada do soquete.
  • Se você fez docker version e viu no strace do daemon que não havia chamada uname() , o daemon nem tentou buscar informações sobre o sistema.
  • Se você vir a chamada write() no strace do daemon, isso significa que o daemon respondeu, mas o cliente não o viu.
  • Talvez este seja um problema conhecido na versão mais antiga do Docker que você está usando. Tente atualizar.
por 13.06.2016 / 19:54