3

Вступление

Я пытаюсь использовать следующую версию 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 docker_git.

Если я пытаюсь запустить 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 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)
}

Куда:

  • 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 Docker и как это можно исправить?
  • Или есть более эффективный способ проверить причину этой ошибки?

1 ответ1

3

Ваш вывод 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, которую вы используете. Попробуйте обновить.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .