エラーメッセージ
http: server gave HTTP response to HTTPS client
プログラムはそれぞれ独自のプレフィックスを付けます。Docker は「Error response from daemon: Get \"https://<registry>/v2/\": http: server gave HTTP response to HTTPS client」、Go のプログラムは「Get \"https://…\": http: server gave …」と出力します。応答が HTTP でもない場合、Go は「tls: first record does not look like a TLS handshake」と報告します。
Go net/http — the TLS handshake received a plain HTTP response (also printed by Docker, Helm, Terraform)
このエラーの意味
Go の HTTP クライアントが TLS ハンドシェイクを開始したところ、サーバーが平文の HTTP レスポンスで応答しました。Go は「HTTP/」のバイト列を認識し、サーバーが HTTPS ではなく HTTP で通信していると報告します。最もよく知られているのは、TLS なしで動いているセルフホストのレジストリに対する docker pull や docker login です。Docker は、ループバックアドレス上にあるか、明示的に insecure と指定されたレジストリを除き、すべてのレジストリに HTTPS を要求します。
発生する理由
TLS なしのコンテナーレジストリ
よくあるテスト用に平文の HTTP で起動した registry:2(distribution)、Harbor、Nexus のレジストリです。Docker、containerd、Podman、Helm、Skopeo は、localhost 以外のすべてに HTTPS を期待します。
HTTP のポートに https:// を使っている
よくあるURL や設定で、https:// とポート 80、8080、または内部サービスのポートを組み合わせている場合です。
TLS が別のポートのプロキシで終端されている
ときどきバックエンドはロードバランサーの背後で HTTP を使って通信しています。ロードバランサーを経由せずに、https:// でバックエンドのポートに直接アクセスしたクライアントに、このエラーが発生します。
修正方法
- 1.
ポートが平文の HTTP で通信していることを確認する
http:// でアクセスして HTTP のステータス行が返ってくれば、それで確認できます。TLS のエンドポイントを使ってください(許容される場合は http:// を使ってください)。
bash curl -si http://registry.example.com:5000/v2/ | head -1 # answers over plain HTTP? - 2.
レジストリを TLS で提供する
レジストリに証明書(パブリックな ACME 経由、または社内 CA 発行のもの)を用意して設定するか、TLS を終端するプロキシを前段に置いてください。
yaml # Docker registry (distribution) config.yml: serve TLS http: addr: :5000 tls: certificate: /certs/registry.example.com.fullchain.pem key: /certs/registry.example.com.key - 3.
最後の手段:レジストリを insecure に指定する(daemon.json)
/etc/docker/daemon.json の insecure-registries にレジストリを記載して dockerd を再起動すると、Docker はそのレジストリと平文の HTTP で通信できるようになります。その場合、イメージと認証情報は暗号化も認証もされずに送られます。隔離された検証用ネットワークでのみ使ってください。
json { "insecure-registries": ["registry.example.com:5000"] }