• Русский
  • Сбой при загрузке образа с unexpected end of JSON input

    Описание проблемы

    При загрузке образов из экземпляра Harbor, доступного через Ingress только по HTTP, среды выполнения контейнеров завершают работу с ошибками вроде:

    error parsing HTTP 404 response body: unexpected end of JSON input: ""

    Пайплайны, использующие тот же Harbor, могут выдавать вариант той же первопричины:

    Error parsing manifest for image: invalid character '<' looking for beginning of value

    Проблема воспроизводится из кластера Kubernetes при использовании containerd / CRI-O / Podman в качестве среды выполнения контейнеров, даже если:

    • адрес Harbor доступен;
    • учетные данные верны, и login выполняется успешно;
    • веб-интерфейс Harbor полностью работает в браузере.

    Причина

    Проблема возникает, когда одновременно выполняются все перечисленные ниже условия:

    1. Harbor опубликован через Ingress только по HTTP (на Ingress не настроен TLS).
    2. Балансировщик нагрузки кластера (LB) прослушивает и порт 80, и порт 443, а на слушателе 443 настроен действующий сертификат по умолчанию.
    3. Клиент, загружающий образ, использует среду выполнения контейнеров, которая следует распространенной схеме «сначала HTTPS, затем HTTP» (containerd, CRI-O, Podman).

    При этих условиях:

    • Среда выполнения сначала пытается использовать HTTPS, так как в ссылке на образ не указан явный протокол или порт.
    • Балансировщик нагрузки принимает TLS-рукопожатие, поскольку на порту 443 доступен сертификат по умолчанию.
    • Но в Ingress нет правила маршрутизации HTTPS для хоста Harbor, поэтому LB возвращает 404 для HTTPS-запроса.
    • Поскольку TLS-рукопожатие прошло успешно, среда выполнения не переключается на HTTP. Она считает 404 окончательным ответом и не может разобрать его как ответ реестра, из-за чего возникает unexpected end of JSON input.

    Устранение неполадки

    Шаг 1 — Проверьте конфигурацию слушателей LB

    Убедитесь, что LB, через который публикуется Harbor, прослушивает и 80, и 443, а на 443 настроен рабочий сертификат по умолчанию. Если порт 443 не прослушивается или TLS-рукопожатие завершается с ошибкой (нет сертификата по умолчанию), эта проблема к вам не относится, и причину следует искать в другом месте.

    Шаг 2 — Повторите проверку с явным указанием протокола

    На узле, где проявляется сбой, попробуйте загрузить образ по HTTP с явным указанием порта. Если этот вариант работает, а вариант по умолчанию без указания схемы — нет, диагноз подтвержден:

    # Неудача — среда выполнения использует HTTPS, получает 404 от LB
    crictl pull --creds <USER>:<PASS> <HARBOR_HOST>/<PROJECT>/<IMAGE>:<TAG>
    
    # Успех — указание порта принудительно использует HTTP
    crictl pull --creds <USER>:<PASS> <HARBOR_HOST>:80/<PROJECT>/<IMAGE>:<TAG>

    Шаг 3 — Проверьте отладочный журнал среды выполнения

    На затронутом узле включите отладочное логирование в среде выполнения контейнеров и найдите сообщения, указывающие на то, что среда выполнения пропустила HTTP-эндпоинт после успешного TLS-рукопожатия, например:

    Skipping non-TLS endpoint http://<HARBOR_HOST> for host/port that appears to use TLS

    Если вы видите это сообщение (или если среда выполнения переключается на HTTPS после успешного TLS-рукопожатия), это подтверждает первопричину.

    Решение

    Обходной путь — явно укажите HTTP-порт в адресе доступа к Harbor

    Явное указание HTTP-порта в адресе доступа к Harbor заставляет клиентов использовать HTTP и обходить эту проблему. Используйте http://<HARBOR_HOST>:80 везде, где настраивается адрес Harbor, чтобы каждая ссылка на образ разрешалась в <HARBOR_HOST>:80/<PROJECT>/<IMAGE>:<TAG>.

    В некоторых случаях, чтобы новое значение адреса вступило в силу, требуется перезапуск клиента — например, рабочие нагрузки, у которых загрузка уже завершилась неудачей, необходимо перезапустить, чтобы они повторили попытку с адресом, в котором явно указан порт.

    Долгосрочное решение — опубликовать Harbor по HTTPS

    Для производственных сред рекомендуемое решение — публиковать Harbor по HTTPS. Полная процедура описана в разделе Configuring HTTPS.

    После правильной настройки HTTPS на Ingress ответ 404 от LB на 443 больше не возвращается, и среда выполнения корректно достигает Harbor без явного указания порта. После того как HTTPS начнет работать, удалите указание порта из адреса интеграции.

    Примечания

    • Одна и та же причина, разные ошибки. Пайплайны, загружающие манифесты, могут сообщать invalid character '<' looking for beginning of value вместо unexpected end of JSON input. В обоих случаях первопричиной является один и тот же HTML-пустой ответ 404.
    • Почему это не исправить в среде выполнения? Поведение «сначала HTTPS, без перехода на HTTP после успешного рукопожатия» является документированной схемой для большинства сред выполнения контейнеров, и Harbor или этот оператор не могут его изменить.