Версия докера зависает перед информацией о сервере

Вступление

Я пытаюсь использовать следующую версию docker на виртуальной машине Linux (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(),
    },
}

От engine-api/types/client.go:

// 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 от докера 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

  1. Получите идентификатор процесса вашего демона Docker. Любая из этих команд будет хранить PID в переменной с именем $DOCKER_PID,

    • Прямо из розетки:

      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}')
      
    • Другой:

      DOCKER_PID=$(ps aux | grep 'docker daemon' | grep -v 'grep' | awk '{print $2}' | head -1)
      
  2. использование strace на демоне Docker, теперь, когда у вас есть PID:

    sudo strace -vvvfts1000 -p $DOCKER_PID
    
  3. В отдельном терминале выполните команду, которая обычно висит в клиенте Docker.

    docker version
    
  4. Смотри strace на демоне Docker, чтобы засвидетельствовать, что происходит, начиная со слушающего конца сокета.

Интерпретация strace выход

Вот что демон должен делать при запуске docker version:

  1. Прочитайте, что отправил клиент:

    [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. Соберите информацию о системе:

    [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. Ответ клиенту с информацией о системе:

    [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. Клиент (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, которую вы используете. Попробуйте обновить.
Другие вопросы по тегам